First let me confess that I'm a beginner with little knowledge of Java. I'm facing a problem with XML to JSON conversion. The XML that I need to covert is complex with elements that will repeat throughout the document. And the order in which the elements appear need to be preserved too. Sample XML :

<?xml version="1.0" encoding="utf-8" ?>

<books>

    <book>

        <chapter>

            <number>i</number>
            
<title>Introduction</title>

        </chapter>
        <volume>
<number>1</number>

            <title>this title contains <italic>markup</italic> tags</title>
            <part>

                
<title>part may contain sections and nested parts</title>

                <section>
                    
<title>contains chapters</title>

                    <chapter>

                        <number>1.1.1</number>
                        
<title>first chapter</title>

                    </chapter>
                    <chapter>

                        <number>1.1.2</number>
                        
<title>second chapter</title>

                    </chapter>
                </section>
            </part>
            <chapter>

                <number>x</number>
                
<title>references for volume one</title>

            </chapter>
        </volume>

        <chapter>

            <number>xii</number>
            
<title>Acknowledgements</title>

        </chapter>
    </book>
</books>

I'm trying to build a Table of contents kind of page and I need this info in JSON format. Since the order is important here I decided that the structure of the resulting JSON should be such that all elements are transformed to similar structure in JSON.

Expected JSON:

{
  "Book": [
    {
      "Type": "chapter",
      "Title": "Introduction",
      "Number": "i",
      "List": ""
    },
    {
      "Type": "volume",
      "Title": "this title contains <![CDATA[<span style='italic'>markup</span>]]> tags",
      "Number": "",
      "List": [
        {
          "Type": "part",
          "Title": "part may contain sections and nested parts",
          "Number":"",
          "List": [
            {
              "Type":"section",
              "Title":"contains chapters",
              "Number":"",
              "List":[
                {
                  "Type":"chapter",
                  "Title":"first chapter",
                  "Number":"1.1.1",
                  "List":""
                }
              ]
            }
            ]
        },
        {
          "Type":"chapter",
          "Title":"last chapter in volume",
          "Number":"x",
          "List":""
        }
      ]
    },
    {
      "Type": "chapter",
      "Title": "last chapter in book",
      "Number": "xii",
      "List": ""
    }
  ]
}    

Two more problems that I'm facing are the data type conversion that occurs when using most JSON libraries like 'org.json'. And the last requirement is that if there are markup tags like '' inside elements then it should be wrapped within CDATA.

I tried the usual ways like org.json XML.toJSONObject() which (as expected) didn't do the trick. Next I tried converting XML to POJO and converting POJO to XML using JAXB. But this also proved ineffective as the structure was lost when the XML is unmarshalled into object. I also tried Staxon but the resulting JSON, even though it retained the original structure, wasn't valid.

Is there a way except using XSLT, to do this conversion?E.g When using JAXB to convert to POJO, is it possible that the use of LinkedHashMaps when unmarshalling will maintain the structure?

Is DOM/SAX the correct way to do this complex transformation? I really don't want to use XSLT to convert XML to JSON as I've faced many issues with this.

Any suggestions welcome. Thanks and sorry for the (very)long post.

Dani AI

Generated

you can avoid XSLT and still keep sibling order by streaming the XML and building exactly the JSON shape you want. is right that Jackson helps, but use it only for the final serialization. Parse with StAX (pull parser) so you control when nodes start/end, keep a stack to manage nesting, and store siblings in a List to preserve order. Also, never rely on a Map to imply order; JSON arrays are the only portable way to keep it.

// model
static class TocNode {
  String Type, Title, Number;
  List<TocNode> List = new ArrayList<>();
  TocNode(String t){ this.Type=t; }
  static boolean isToc(String n){ return "chapter".equals(n)||"volume".equals(n)||"part".equals(n)||"section".equals(n); }
}

// parse
XMLStreamReader r = XMLInputFactory.newInstance().createXMLStreamReader(in);
Deque<TocNode> stack = new ArrayDeque<>();
List<TocNode> book = new ArrayList<>();
String field = null;

while (r.hasNext()) {
  int ev = r.next();
  if (ev == XMLStreamConstants.START_ELEMENT) {
    String n = r.getLocalName();
    if (TocNode.isToc(n)) stack.push(new TocNode(n));
    else if ("title".equals(n) || "number".equals(n)) field = n;
  } else if (ev == XMLStreamConstants.CHARACTERS && field != null && !r.isWhiteSpace()) {
    String txt = r.getText();
    if ("number".equals(field)) stack.peek().Number = txt.trim();   // keep as String
    else stack.peek().Title = txt;                                  // see note on markup
  } else if (ev == XMLStreamConstants.END_ELEMENT) {
    String n = r.getLocalName();
    if ("title".equals(n) || "number".equals(n)) field = null;
    else if (TocNode.isToc(n)) { TocNode done = stack.pop();
      if (stack.isEmpty()) book.add(done); else stack.peek().List.add(done);
    }
  }
}

// write JSON in field order
ObjectMapper m = new ObjectMapper();
m.writeValue(out, Collections.singletonMap("Book", book));

Notes:

  • Titles with nested markup: JSON has no CDATA. Either keep the inner HTML as a plain string (recommended), or, if you must emit CDATA markers, wrap it yourself when you detect < in the title: title = "<![CDATA[" + innerXml + "]]>";. To preserve inner markup, capture the entire inner-XML of <title> instead of r.getText().
  • Type coercion: always set Number as a String (as above). When building JSON nodes manually, call put(name, valueAsString).

You can use Jackson library for conversion XML to JSON.
Example: http://stackoverflow.com/questions/6746059/parsing-xml-into-json
If you still have problems, I suggest to make some Java classes that will represent custom data, and them just to map xml to them. After that using Jackson it is easy zo covert everything to JSON.
If you have any questions feel free to ask.
Regards, Mike.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.