AJAX with CKEditor in Spring Boot

1. Overview

In this article, we will cover how to use CKEditor with Spring Boot. In this tutorial, we will be importing an XML document with numerous data, program the ability to load a set of data to the CKEditor instance with a GET request, and do a POST request to save the CKEditor’s data.

Technologies we will be using include MongoDB, Thymeleaf, and Spring Batch.

The full source code for this tutorial is available on Github.

2. What is CKEditor?

CKEditor is a browser-based What-You-See-Is-What-You-Get (WYSIWYG) content editor.  CKEditor aims to bring to a web interface common word processor features found in desktop editing applications like Microsoft Word and OpenOffice.

CKEditor has numerous features for end users in regards to the user interface, inserting content, authoring content, and more.

There are different versions of CKEditor, but for this tutorial we are using CKEditor 4. To see a demo, visit: https://ckeditor.com/ckeditor-4/

3. The XML Document

As mentioned, we are uploading an XML document in this application. The XML data will be inserted into the database and used for the rest of the tutorial.

<?xml version="1.0"?>
<Music xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" id="MUS-1" style="1.1">
<status date="2017-11-07">draft</status>
 <title xmlns:xhtml="http://www.w3.org/1999/xhtml" >Guide to Music I Like - No Specific Genre</title>
  <description xmlns:xhtml="http://www.w3.org/1999/xhtml" >This guide presents a catalog of music that can be found on Spotify. 
    <html:br xmlns:html="http://www.w3.org/1999/xhtml"/>
    <html:br xmlns:html="http://www.w3.org/1999/xhtml"/>
    This is a very small sample of music found on Spotify and is no way to be considered comprehensive.
    Run the Jewels
    <song-title>Legend Has It</song-title>
    Kendrick Lamar
    Weird Al Yankovic
    <song-title>NOW That's What I Call Polka!</song-title>
    Eiffel 65
    <song-title>Blue (Da Ba Dee) - DJ Ponte Ice Pop Radio</song-title>
    <song-title>Hacker Music</song-title>
    Raise Your Flag
    GZA, Method Man

4. Model

For the above XML code, we can model a Song like this:

public class SongModel {
    private String id;
    private String artist;
    private String songTitle;
    private Boolean updated;
    public Boolean getUpdated() {
        return updated;
    public void setUpdated(Boolean updated) {
        this.updated = updated;
    public String getArtist() {
        return artist;
    public void setArtist(String artist) {
        this.artist = artist;
    public String getSongTitle() {
        return songTitle;
    public void setSongTitle(String songTitle) {
        this.songTitle = songTitle;
    public String getId() {
        return id;
    public void setId(String id) {
        this.id = id;
    public SongModel(
        @JsonProperty("artist") String artist,
        @JsonProperty("song-title") String songTitle){
        this.artist = artist;
        this.songTitle = songTitle;
    public String toString() {
      return "Person [id=" + id + ", artist=" + artist + ", song-title=" + songTitle + "]";


For our application, we will be differentiating between an unmodified song and a song that has been modified in CKEditor with a separate Model and Repository.

Let’s now define what an Updated Song is:

public class UpdatedSong {
    private String id;
    private String artist;
    private String songTitle;
    private String html;
    private String sid;
    public String getSid() {
        return sid;
    public void setSid(String sid) {
        this.sid = sid;
    public String getId() {
        return id;
    public void setId(String id) {
        this.id = id;
    public String getArtist() {
        return artist;
    public void setArtist(String artist) {
        this.artist = artist;
    public String getSongTitle() {
        return songTitle;
    public void setSongTitle(String songTitle) {
        this.songTitle = songTitle;
    public String getHtml() {
        return html;
    public void setHtml(String html) {
        this.html = html;


5. File Upload and Processing

As this article’s focus is on CKEditor and AJAX, we aren’t going to go into detail on the file upload and processing with Spring Batch. We have reviewed this process in depth in these prior posts, however:

6. Setting Data to CKEditor – GET Request

It is our goal to retrieve the data for an individual song and display that data in CKEditor. There are two issues to tackle: retrieving the data for an individual song and displaying it in CKEditor.

6.1 Client Side Code

In view.html, we use a table in Thymeleaf to iterate through each Song in the Song repository. To be able to retrieve the data from the server for a specific song, we pass in the Song’s id to a function.

Here’s the snippet of code that is responsible for calling the function that retrieves the data from the server and subsequently sets the data to the CKEditor instance:

<table class="table datatable">
<th>Song Title</th>
<tr th:each="songList : ${songList}">
<td th:text="${songList.artist}">Text ...</td>
<td th:text="${songList.songTitle}">Text ...</td>
<td><button th:onclick="|getSong('${songList.id}')|" id="button" class="btn btn-primary btn-condensed">
<i class="glyphicon glyphicon-folder-open"></i>

As we can see, the id of Song is essential for us to be able to retrieve the data.

In the getSong function, we use a deferred promise to ensure that data is set after the GET request:

		function getSong(song) {
				url : "/api/show/?sid=" + song,
				type : 'GET',
				dataType : 'text'
			}).then(function(data) {
				var length = data.length-2;
				var datacut = data.slice(9,length);


			$("#form").attr("action", "/api/save/?sid=" + song);


6.2 Server Side Code

getSong accepts a parameter named sid, which stands for Song id. sid is also a path variable in the @GetMapping. We treat sid as a String because this is the id of the Song from MongoDB.

We check if the Song has been modified and if so we retrieve the associated UpdatedSong entity. If not, we treat the Song differently. Ultimately, we return a simple POJO with a String for data named ResponseModel, however:

    public ResponseEntity<?> getSong(@RequestParam String sid, Model model){
        ResponseModel response = new ResponseModel();
        System.out.println("SID :::::" + sid);
        ArrayList<String> musicText = new ArrayList<String>();
            String sidString = sid;
            SongModel songModel = songDAO.findOne(sidString);
            System.out.println("get status of boolean during get ::::::" + songModel.getUpdated());
            if(songModel.getUpdated()==false ){
                String filterText = format.changeJsonToHTML(musicText);
            } else if(songModel.getUpdated()==true){
                UpdatedSong updated = updatedDAO.findBysid(sidString);
                String text = updated.getHtml();
                System.out.println("getting the updated text ::::::::" + text);

        model.addAttribute("response", response);
        return ResponseEntity.ok(response);

ResponseModel is a very simple POJO as mentioned:

public class ResponseModel {
    private String data;
    public ResponseModel(){
    public ResponseModel(String data){
            this.data = data;

    public String getData() {
            return data;

    public void setData(String data) {
            this.data = data;


7. Saving CKEditor Data – POST Request

POSTing the data isn’t much of a challenge; however, ensuring that the data is handled appropriately can be.

7.1 Client Side Code

As the CKEditor instance is a textarea within a form, we can trigger a function on a form submit:

function() {

$("#form").submit(function(event) {
// Prevent the form from submitting via the browser.

ajaxPost() retrieves the current data in the CKEditor and sets it to the variable formData:

function ajaxPost() {

var formData = CKEDITOR.instances.content

type : "POST",
contentType : "text/html",
url : $("#form").attr("action"),
data : formData,
dataType : 'text',
success : function(result) {


+ "Post Successfully! "
+ "


error : function(e) {
console.log("ERROR: ", e);



It is important to note:

  • contentType is “text/html”
  • dataType is “text”

Having the incorrect contentType or dataType can lead to errors or malformed data.

7.2 Server Side Code

We stated in our contentType for the POST request that the mediatype is “text/html”.  We need to specify in our mapping that this will be consumed. Therefore, we add consumes = MediaType.TEXT_HTML_VALUE with our @PostMapping.

Areas for us to note include:

  • @RequestBody String body is responsible for setting the variable body to the content of our request
  • We once again return ResponseModel, the simple POJO described earlier that contains our data
  • We treat a previously modified SongModel different than one that has not been modified before

Also, like the GET request, the sid allows us to deal with the correct Song:

 @PostMapping(value={"/save/","/save/[sid]"}, consumes = MediaType.TEXT_HTML_VALUE)
    public @ResponseBody ResponseModel saveSong( @RequestBody String body, @RequestParam String sid){
        ResponseModel response = new ResponseModel();
        SongModel oldSong = songDAO.findOne(sid);
        String songTitle = oldSong.getSongTitle();
        String artistName = oldSong.getArtist();
        if(oldSong.getUpdated() == false){
            UpdatedSong updatedSong = new UpdatedSong();
            System.out.println("get status of boolean during post :::::" + oldSong.getUpdated());
            UpdatedSong currentSong = updatedDAO.findBysid(sid);
        return response;

8. Demo

We visit localhost:8080:

home page for example ckeditor spring boot application

We upload the provided music-example.xml file:

Screen shown after upload for example ckeditor application

We click “Load” for a song:

loaded content into ckeditor

We add content and click “Save”:

we add content and click save ckeditor

If you return to the saved content, you may see “\n”for line breaks. For the time being, discussing this is out of the scope for the tutorial.

9. Conclusion

In this tutorial, we covered how to load  data using a GET request with the object’s id, set the data to the CKEditor instance, and save the CKEditor’s data back to the database with a POST request. There’s extra code, such as using two different entities for the data (an original and a modified version), that isn’t necessary, but hopefully is instructive.

The full code can be found on Github.

Converting XML to JSON + Raw Use in MongoDB + Spring Batch


Why convert XML to JSON for raw use in MongoDB?

Since MongoDB uses JSON documents in order to store records, just as tables and rows store records in a relational database, we naturally need to convert our XML to JSON.

Some applications may need to store raw (unmodified) JSON because there is uncertainty in how the data will be structured.

There are hundreds of XML-based standards. If an application is to process XML files that do not follow the same standard, there is uncertainty in how the data will be structured.

Why use Spring Batch?

Spring Batch provides reusable functions that are essential in processing large volumes of records and other features that enable high-volume and high performance batch jobs. The Spring Website has documented Spring Batch well.

For another tutorial on Spring Batch, see my previous post on Processing CSVs with Spring Batch.

0 – Converting XML to JSON For Use In MongoDB With Spring Batch Example Application

The example application converts an XML document that is a “policy” for configuring a music playlist. This policy is intended to resemble real cyber security configuration documents. It is a short document but illustrates how you will search complex XML documents.

The approach we will be taking our tutorial is for handling XML files of varying style. We want to be able to handle the unexpected. This is why we are keeping the data “raw.”

View and Download the code from Github

1 – Project Structure

It is a typical Maven structure. We have one package for this example application. The XML file is in src/main/resources.
Project structure of converting xml to json with spring batch mongodb

2 – Project Dependencies

Besides our typical Spring Boot dependencies, we include dependencies for an embedded MongoDB database and for processing JSON.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">


	<description>Michael C  Good - XML to JSON + MongoDB + Spring Batch Example</description>

		<relativePath /> <!-- lookup parent from repository -->






3 – XML Document

This is the example policy document created for this tutorial. It’s structure is based on real cyber security policy documents.

  • Note the parent of the document is the Policy tag.
  • Important information lies within the Group tag.
  • Look at the values that reside within the tags, such as the id in Policy or the date within status.

There’s a lot of information condensed in this small document to consider. For instance, there is also the XML namespace (xmlns). We won’t touch on this in the rest of the tutorial, but depending on your goals it could be something to add logic for.

<?xml version="1.0"?>
<Policy  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" style="STY_1.1" id="NRD-1">
  <status date="2017-10-18">draft</status>
  <title xmlns:xhtml="http://www.w3.org/1999/xhtml">Guide to the Configuration of Music Playlist</title>
   <description xmlns:xhtml="http://www.w3.org/1999/xhtml" >This guide presents a catalog of relevant
    configuration settings for a playlist that I listen to while I work on software development.
    <html:br xmlns:html="http://www.w3.org/1999/xhtml"/>
    <html:br xmlns:html="http://www.w3.org/1999/xhtml"/>
    Providing myself with such guidance reminds me how to efficiently
    configure my playlist.  Lorem ipsum <html:i xmlns:html="http://www.w3.org/1999/xhtml">Lorem ipsum,</html:i> 
    and Lorem ipsum.  Some example
    <html:i xmlns:html="http://www.w3.org/1999/xhtml">Lorem ipsum</html:i>, which are Lorem ipsum.
  <Group id="remediation_functions">
    <title xmlns:xhtml="http://www.w3.org/1999/xhtml" >Remediation functions used by the SCAP Security Guide Project</title>
    <description xmlns:xhtml="http://www.w3.org/1999/xhtml" >XCCDF form of the various remediation functions as used by
      remediation scripts from the SCAP Security Guide Project</description>
    <Value id="is_the_music_good" prohibitChanges="true" >
      <title xmlns:xhtml="http://www.w3.org/1999/xhtml" >Remediation function to fix bad playlist</title>
      <description xmlns:xhtml="http://www.w3.org/1999/xhtml" >Function to fix bad playlist.
       Lorem ipsum Lorem ipsum Lorem ipsum Lorem ipsum
       Lorem ipsum
       Lorem ipsum
       Lorem ipsum
       Lorem ipsum
        function fix_bad_playlist {
        # Load function arguments into local variables
       Lorem ipsum
       Lorem ipsum
       Lorem ipsum
        # Check sanity of the input
        if [ $# Lorem ipsum ]
        echo "Usage: Lorem ipsum"
        echo "Aborting."
        exit 1

4 – MongoDB Configuration

Below we specify that we are using an embedded MongoDB database, make it discoverable for a component scan that is bundled in the convenience annotation @SpringBootApplication, and specify that mongoTemplate will be a bean.

package com.michaelcgood;

import java.io.IOException;
import cz.jirutka.spring.embedmongo.EmbeddedMongoFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.core.*;
import com.mongodb.MongoClient;
public class MongoConfig {
    private static final String MONGO_DB_URL = "localhost";
    private static final String MONGO_DB_NAME = "embeded_db";
    public MongoTemplate mongoTemplate() throws IOException {
        EmbeddedMongoFactoryBean mongo = new EmbeddedMongoFactoryBean();
        MongoClient mongoClient = mongo.getObject();
        MongoTemplate mongoTemplate = new MongoTemplate(mongoClient, MONGO_DB_NAME);
        return mongoTemplate;

5 – Processing XML to JSON

step1() of our Spring Batch Job contains calls three methods to help process the XML to JSON. We will review each individually.

    public Step step1() {
        return stepBuilderFactory.get("step1")
                .tasklet(new Tasklet() {
                    public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
                        // get path of file in src/main/resources
                        Path xmlDocPath =  Paths.get(getFilePath());
                        // process the file to json
                         String json = processXML2JSON(xmlDocPath);
                         // insert json into mongodb
                        return RepeatStatus.FINISHED;

5.1 – getFilePath()

This method simply gets the file path that is passed as a parameter to the method processXML2JSON.

  • ClassLoader is helping us locate the XML file in our resources folder.
 // no parameter method for creating the path to our xml file
    private String getFilePath(){
        String fileName = "FakePolicy.xml";
        ClassLoader classLoader = getClass().getClassLoader();
        File file = new File(classLoader.getResource(fileName).getFile());
        String xmlFilePath = file.getAbsolutePath();
        return xmlFilePath;

5.2 – processXML2JSON(xmlDocPath)

The string returned by getFilePath is passed into this method as a parameter. A JSONOBject is created from a String of the XML file.

   // takes a parameter of xml path and returns json as a string
    private String processXML2JSON(Path xmlDocPath) throws JSONException {
        String XML_STRING = null;
        try {
            XML_STRING = Files.lines(xmlDocPath).collect(Collectors.joining("\n"));
        } catch (IOException e) {
        JSONObject xmlJSONObj = XML.toJSONObject(XML_STRING);
        String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
        System.out.println("PRINTING STRING :::::::::::::::::::::" + jsonPrettyPrintString);
        return jsonPrettyPrintString;

5.3 – insertToMongo(json)

We insert the parsed JSON into a MongoDB document. We then insert this document with the help of the @Autowired mongoTemplate into a collection named “foo”.

   // inserts to our mongodb
    private void insertToMongo(String jsonString){
        Document doc = Document.parse(jsonString);
        mongoTemplate.insert(doc, "foo");

6 – Querying MongoDB

step2() of our Spring Batch Job contains our MongoDB queries.

  • mongoTemplate.collectionExists returns a Boolean value based on the existence of the collection.
  • mongoTemplate.getCollection(“foo”).find() returns all the documents within the collection.
  • alldocs.toArray() returns an array of DBObjects.
  • Then we call three methods that we will review individually below.
 public Step step2(){
        return stepBuilderFactory.get("step2")
            .tasklet(new Tasklet(){
            public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception{
                // all printing out to console removed for post's brevity
                // checks if our collection exists
                Boolean doesexist = mongoTemplate.collectionExists("foo");
                // show all DBObjects in foo collection
                DBCursor alldocs = mongoTemplate.getCollection("foo").find();
                List<DBObject> dbarray = alldocs.toArray();
                // execute the three methods we defined for querying the foo collection
                String result = doCollect();
                String resultTwo = doCollectTwo();
                String resultThree = doCollectThree();
                return RepeatStatus.FINISHED;

6.1 – First query

The goal of this query is to find a document where style=”STY_1.1″. To accomplish this, we need to remember where style resides in the document. It is a child of Policy; therefore, we address it in the criteria as Policy.style.

The other goal of this query is to only return the id field of the Policy. It is also just a child of Policy.

The result is returned by calling this method: mongoTemplate.findOne(query, String.class, “foo”);. The output is a String, so the second parameter is String.class. The third parameter is our collection name.

    public String doCollect(){
        Query query = new Query();
        String result = mongoTemplate.findOne(query, String.class, "foo");
        return result;

6.2 – Second query

The difference between the second query and the first query are the fields returned. In the second query, we return Value, which is a child of both Policy and Group.

    public String doCollectTwo(){
        Query query = new Query();
        String result = mongoTemplate.findOne(query, String.class, "foo");
        return result;

6.3 – Third query

The criteria for the third query is different. We only want to return a document with the id “NRD-1” and a status date of “2017-10-18”. We only want to return two fields: title and description, which are both children of Value.

Referring to the XML document or the printed JSON in the demo below for further clarification on the queries.

    public String doCollectThree(){
        Query query = new Query();
        String result = mongoTemplate.findOne(query, String.class, "foo");
        return result;

7 – Spring Batch Job

The Job begins with step1 and calls step2 next.

    public Job xmlToJsonToMongo() {
        return jobBuilderFactory.get("XML_Processor")

8 – @SpringBootApplication

This is a standard class with static void main and @SpringBootApplication.

package com.michaelcgood;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

public class SpringBatchMongodb {

	public static void main(String[] args) {
		SpringApplication.run(SpringBatchMongodb.class, args);

9 – Demo

9.1 – step1

The JSON is printed as a String. I have cut the output past description below because it is long.

Executing step: [step1]
PRINTING STRING :::::::::::::::::::::{"Policy": {
    "Group": {
        "Value": {
            "prohibitChanges": true,
            "description": {

9.2 – step2

I have cut the results to format the output for the blog post.

Executing step: [step2]

Checking if the collection exists

Status of collection returns :::::::::::::::::::::true

Show all objects

list of db objects returns:::::::::::::::::::::[{ "_id" : { "$oid" : "59e7c0324ad9510acf5773c0"} , [..]

Just return the id of Policy

RESULT:::::::::::::::::::::{ "_id" : { "$oid" : "59e7c0324ad9510acf5773c0"} , "Policy" : { "id" : "NRD-1"}}

To see the other results printed to the console, fork/download the code from Github and run the application.

10 – Conclusion

We have reviewed how to convert XML to JSON, store the JSON to MongoDB, and how to query the database for specific results.

Further reading:

The source code is on Github