'How to convert an XML structured-like object to a JSON string in Java?

So I have an XML structured object (Java) and need some help to convert it to a JSON string without using any library and in an iterative way.

E.g

<root>
    <a/>
    <b/>
    <a/>
    <a>
        <c/>
    <a/>
    <d>
        ...
    </d>
<root/>

Which could be represented in a class (Java) like:

class Element {
    String name; (root)
    List<Element> childs; (a, b, a, a, d)(i.e. direct childs)
}

And should be in JSON:

{
  "root":{
           "a":[
                 {},
                 {},
                 {
                   "c":{}
                 }
               ],
           "b": {},
           "d": {
                 ...
                }
         }
}

The depth can be infinite (not really but it can be really deep) and the JSON string does not need to be indented or keep the same order, it just need to be valid JSON as the example. The difficult thing (except to make it an iterative solution) is that several elements with the same name at the same level should be an array in the JSON since JSON does not like it when there are elements with the same name at the same level.

Edit: I'm not looking for a library, as I stated earlier. Besides, the library many of you mentioned uses recursion which is not what I want either. And I'm not converting an actual XML, instead it's an XML structured-like object, i.e. it can be nested with child elements with the same name at the same level, different depths etc. Like in the example. Thanks though!



Solution 1:[1]

So to convert it to a JSON string in an iterative way I did a preorder traversal with some modification/adjustments.

Firstly a created a help class, let's call it Node, that was used for traversing the structures. So each Element would be wrapped in such class. This class helped me to mark the nodes with certain values and to group the elements with the same name, more on this later.

The next thing was to use two data structures, a Set and a Stack (or Deque). The Set was used to keep track on the visisted/traversed nodes and the Stack was used for the traversal (the next node to traverse to in the next iteration).

Then I wrapped the root element as a Node and pushed it to the stack and the preorder traversal could begin (actually, I also added an opening { before starting the traversal). In each iteration, the child elements were "grouped" and wrapped as nodes and then pushed to the stack. With "grouped" I mean that if there was more than one child element with the same name, they were put in a list in the Node and the node was marked as a list.

After the child elements had been pushed into the stack I did the following things:

  1. If the currently visisted node is not marked as being a part of a list, I appended: <name of the node/element> : to the JSON string result. This is because if it was a part of a list, then the name should not be printed out for each node since the name should only be printed out once in beginning for arrays in JSONs, e.g: "array":[{...},..,{...}].
  2. If the currently visited node is not a list, I appended { to the result and also appended the fields/attributes if there were any.
  3. Else, if the currently visited node is a list, I appended [ and wrapped each of its grouped child elements as Nodes, marked them as being a part of a list, and pushed them to the stack (marking them this way helped in the next iterations in step 1.).
  4. Add the currently visisted node to the Set.

What about the closing parentheses? In the beginning of each iteration I used the Set to check if the currently (topmost/peek) node had already been visited (see step 4 above), if it had it could only mean one thing: it's time to close and pop the stack. If the node was marked as a list, we would close it with ], otherwise with }. Then, if the next node (peek) is not visited, it means we are moving "sideways" and should append , to the result. Otherwise, if the stack is empty, a final closing } is appended. Then, go to the next iteration.

I know it is a long answer and pseudocode would have probably been better (or the actual code which I cannot show... yes it works!), but I tried to explain the best I could. This solution might not work for all cases. For example, there must be one and only one root element (as in XML). This however worked for me. It was basically a simple preorder traversal... Thanks for all the help and of course if someone has an opinion, a better solution, or some cases that this might not work, I would be very happy to hear!

Solution 2:[2]

I'm not sure what you mean by "without using any library", and I'm not sure what you mean by "iterative" or "optimal".

XML and JSON have different data models, and there's no perfect optimal way of converting one to the other. There are many different libraries that do a reasonably good job, but they all have limitations. One of the difficulties you mention is XML elements that have multiple children with the same name (like a <div> containing multiple <p> elements). At first sight it ertermakes sense to turn the <p> elements into a array. But then what do you do if there's a <div> that only has one <p> child? Every converter finds different answers to this problem, and none of them is perfect.

Saying you don't want to use any library implies you want to write your own converter. That's not a crazy idea, because you can then adapt the conversion rules to the nature of your particular data model. And you can then decide what "optimal" means for you.

But your question really seems to be "please tell me what conversion rules I should apply", and the only answer to that is that there are no conversion rules that work well for everyone.

Solution 3:[3]

import org.json.JSONObject;

JSONObject xmlJsonObj = XML.toJSONObject(new String(buf, "utf-8"));

or try this

XML.toJSONObject(xml_text).toString();

either of these should work.

download from here JSON JAR

Solution 4:[4]

Jackson is a good library to convert XML to JSON in Java. Please check this Jackson tutorial.

Solution 5:[5]

Michael Kay is right. You need to build your own algo to go through your specific structure to write your specific JSON. Walking through a tree-like structure is a kind of recursion. Of course, it can be transformed into iterative form if required, but recursion seems to be more natural. Anyway, imo, it is preferrable to use stream/event/token based API to generate JSON rather than any of object mappers. For example, using your Element structure and a simple JSON parser/generator https://github.com/anatolygudkov/green-jelly:

import org.green.jelly.AppendableWriter;
import org.green.jelly.JsonGenerator;

import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ToJson {
    static class Element {
        final String name;
        final List<Element> children = new ArrayList<>();

        Element(final String name) {
            this.name = name;
        }

        void toJson(final JsonGenerator generator) {
            toJson(generator, false);
        }

        private void toJson(final JsonGenerator generator, boolean isArray) {
            if (!isArray) {
                generator.objectMember(name);
            }
            generator.startObject();

            children.stream()
                    .collect(Collectors.groupingBy(element -> element.name, Collectors.toList()))
                    .forEach((name, groupedByNameElements) -> {
                        if (groupedByNameElements.size() == 1) {
                            groupedByNameElements.get(0).toJson(generator, false);
                            return;
                        }
                        generator.objectMember(name);
                        generator.startArray();
                        groupedByNameElements.stream().forEach(element -> element.toJson(generator, true));
                        generator.endArray();
                    }
            );

            generator.endObject();
        }
    }

    public static void main(String[] args) {
        final Element root = new Element("root");
        root.children.add(new Element("a"));
        root.children.add(new Element("b"));
        root.children.add(new Element("a"));
        final Element aWithChild = new Element("a");
        root.children.add(aWithChild);
        aWithChild.children.add(new Element("c"));
        final Element dWithChildren = new Element("d");
        root.children.add(dWithChildren);
        dWithChildren.children.add(new Element("e"));
        final Element eWithChildren = new Element("e");
        dWithChildren.children.add(eWithChildren);
        eWithChildren.children.add(new Element("f"));
        eWithChildren.children.add(new Element("f"));

        final StringWriter result = new StringWriter();

        final JsonGenerator generator = new JsonGenerator(false);
        generator.setOutput(new AppendableWriter<>(result));
        generator.startObject();
        root.toJson(generator);
        generator.endObject();
        generator.eoj();

        System.out.println(result);
    }
}

The code produces

{"root":{"a":[{},{},{"c":{}}],"b":{},"d":{"e":[{},{"f":[{},{}]}]}}}

Also note, that your algo will depend on your specific data/element structure. For example, you could store children already ordered and grouped by name and so on...

Solution 6:[6]

Underscore-java library has a static method U.xmlToJson(xml).

<root>
    <a/>
    <b/>
    <a/>
    <a>
        <c/>
    </a>
    <d>
        ...
    </d>
</root>

may be converted to the

{
  "root": {
    "a": [
      {
        "-self-closing": "true"
      },
      {
        "#item": {
          "b": {
            "-self-closing": "true"
          }
        }
      },
      {
        "-self-closing": "true"
      },
      {
        "c": {
          "-self-closing": "true"
        }
      }
    ],
    "d": "\n        ...\n    "
  },
  "#omit-xml-declaration": "yes"
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Antonio
Solution 2 Michael Kay
Solution 3 JustCode
Solution 4 ganeshbhargav
Solution 5
Solution 6