如何将一个类似于XML结构的对象转换成Java中的JSON字符串?

huangapple go评论81阅读模式
英文:

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

问题

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

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!

答案1

得分: 3

我不确定你所说的“不使用任何库”是什么意思,也不确定你所说的“迭代”或“最优”的含义。

XML和JSON具有不同的数据模型,没有一种完美的转换方法能够将一个格式转换为另一个格式。有许多不同的库可以做出合理的转换,但它们都有局限性。你提到的困难之一是XML元素具有多个相同名称的子元素(例如包含多个<p>元素的<div>)。乍一看,将<p>元素转换为数组似乎是有道理的。但是,如果有一个只有一个<p>子元素的<div>元素呢?每个转换器对这个问题的解决方案都不同,并且没有一个是完美的。

说你不想使用任何库意味着你想要编写自己的转换器。这并不是一个荒谬的想法,因为你可以根据你特定的数据模型来调整转换规则。然后,你可以决定什么是对你而言的“最优”。

但是你的问题似乎真正是“请告诉我应该应用哪些转换规则”,唯一的答案是,并没有适用于所有人的转换规则能够表现良好。

英文:

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 &lt;div&gt; containing multiple &lt;p&gt; elements). At first sight it ertermakes sense to turn the &lt;p&gt; elements into a array. But then what do you do if there's a &lt;div&gt; that only has one &lt;p&gt; 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.

答案2

得分: 0

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][1]
英文:

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

答案3

得分: 0

Jackson是一个在Java中将XML转换为JSON的优秀库。
请查看此链接上的Jackson教程。

英文:

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

答案4

得分: 0

迈克尔·凯是对的。您需要构建自己的算法,以遍历您特定的结构,以编写特定的JSON。遍历类似树状结构的操作是一种递归。当然,如果需要的话,它可以转换为迭代形式,但递归似乎更加自然。无论如何,在我看来,最好使用基于流/事件/令牌的API来生成JSON,而不是使用任何对象映射器。例如,使用您的Element结构和一个简单的JSON解析器/生成器 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);
    }
}

该代码生成的结果如下:

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

此外,请注意,您的算法将取决于您特定的数据/元素结构。例如,您可以根据名称对孩子进行排序和分组等等...

英文:

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&lt;Element&gt; children = new ArrayList&lt;&gt;();
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 -&gt; element.name, Collectors.toList()))
.forEach((name, groupedByNameElements) -&gt; {
if (groupedByNameElements.size() == 1) {
groupedByNameElements.get(0).toJson(generator, false);
return;
}
generator.objectMember(name);
generator.startArray();
groupedByNameElements.stream().forEach(element -&gt; element.toJson(generator, true));
generator.endArray();
}
);
generator.endObject();
}
}
public static void main(String[] args) {
final Element root = new Element(&quot;root&quot;);
root.children.add(new Element(&quot;a&quot;));
root.children.add(new Element(&quot;b&quot;));
root.children.add(new Element(&quot;a&quot;));
final Element aWithChild = new Element(&quot;a&quot;);
root.children.add(aWithChild);
aWithChild.children.add(new Element(&quot;c&quot;));
final Element dWithChildren = new Element(&quot;d&quot;);
root.children.add(dWithChildren);
dWithChildren.children.add(new Element(&quot;e&quot;));
final Element eWithChildren = new Element(&quot;e&quot;);
dWithChildren.children.add(eWithChildren);
eWithChildren.children.add(new Element(&quot;f&quot;));
eWithChildren.children.add(new Element(&quot;f&quot;));
final StringWriter result = new StringWriter();
final JsonGenerator generator = new JsonGenerator(false);
generator.setOutput(new AppendableWriter&lt;&gt;(result));
generator.startObject();
root.toJson(generator);
generator.endObject();
generator.eoj();
System.out.println(result);
}
}

The code produces

{&quot;root&quot;:{&quot;a&quot;:[{},{},{&quot;c&quot;:{}}],&quot;b&quot;:{},&quot;d&quot;:{&quot;e&quot;:[{},{&quot;f&quot;:[{},{}]}]}}}

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...

答案5

得分: 0

因此,为了以迭代的方式将其转换为JSON字符串,我进行了一次先序遍历,并进行了一些修改和调整。

首先,我创建了一个辅助类,我们称之为Node,用于遍历这些结构。因此,每个Element都会被包装在这样的类中。这个类帮助我使用特定的值标记节点,并将具有相同名称的元素分组,稍后会详细说明。

接下来的事情是使用两个数据结构,一个集合和一个栈(或双端队列)。集合用于跟踪访问/遍历过的节点,而栈用于遍历(下一次迭代中要遍历的下一个节点)。

然后,我将根元素包装为一个Node,并将其推入栈中,先序遍历就可以开始了(实际上,在开始遍历之前,我还添加了一个开头的{)。在每次迭代中,子元素都会被“分组”,并被包装为节点,然后推入栈中。这里的“分组”是指,如果有多个具有相同名称的子元素,它们会被放入Node的列表中,并且该节点会被标记为列表。

在将子元素推入栈中之后,我执行了以下操作:

  1. 如果当前访问的节点标记为列表的一部分,我将<节点/元素的名称>:附加到JSON字符串结果中。这是因为如果它是列表的一部分,那么在每个节点中名称不应该被打印出来,因为在JSON数组的开头只应该打印一次名称,例如:"array":[{...},..,{...}]
  2. 如果当前访问的节点不是列表,我将{附加到结果中,并且如果有任何字段/属性,也会将它们附加到结果中。
  3. 否则,如果当前访问的节点列表,则我将[附加到结果中,并将其每个分组的子元素都包装为Node,将它们标记为列表的一部分,并将它们推入栈中(以这种方式标记它们有助于在下一次迭代的步骤1中使用)。
  4. 将当前访问的节点添加到集合中。

那么闭合括号怎么办呢?在每次迭代开始时,我使用集合来检查当前(最顶部/最前面)的节点是否已经被访问过(参见上面的步骤4),如果是,那只能意味着一件事:是时候关闭并弹出栈了。如果该节点被标记为列表,则使用]进行闭合,否则使用}进行闭合。然后,如果下一个节点(顶部/最前面)被访问,则意味着我们正在“侧向”移动,应该在结果中附加,。否则,如果栈为空,则附加最终的闭合}。然后,进入下一次迭代。

我知道这是一个很长的回答,伪代码可能更好一些(或者实际代码,我不能展示...是的,它有效!),但我尽量解释得最清楚。这个解决方案可能不适用于所有情况。例如,必须有且仅有一个根元素(就像XML中一样)。然而,这对我来说是有效的。基本上,这就是一个简单的先序遍历... 感谢所有的帮助,当然,如果有人有意见,有更好的解决方案,或者有一些可能不起作用的情况,我会非常乐意听取!

英文:

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: &lt;name of the node/element&gt; : 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: &quot;array&quot;:[{...},..,{...}].
  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!

答案6

得分: 0

以下是您提供的内容的翻译:

Underscore-java 库有一个静态方法 U.xmlToJson(xml)。

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

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

&lt;root&gt;
&lt;a/&gt;
&lt;b/&gt;
&lt;a/&gt;
&lt;a&gt;
&lt;c/&gt;
&lt;/a&gt;
&lt;d&gt;
...
&lt;/d&gt;
&lt;/root&gt;

may be converted to the

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

huangapple
  • 本文由 发表于 2020年10月22日 23:48:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/64485884.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定