简体   繁体   English

如何在 Java 中将类似 XML 结构的对象转换为 JSON 字符串?

[英]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.因此,我有一个 XML 结构化对象 (Java),需要一些帮助才能在不使用任何库的情况下以迭代方式将其转换为 JSON 字符串。

Eg例如

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

Which could be represented in a class (Java) like:可以在类(Java)中表示,例如:

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

And should be in JSON:并且应该在 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.深度可以是无限的(不是真的,但它可以是真的很深)并且 JSON 字符串不需要缩进或保持相同的顺序,它只需要是有效的 JSON 作为示例。 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.难点(除了让它成为一个迭代的解决方案)是,同级别的几个同名元素应该是 JSON 中的一个数组,因为当同级别存在同名元素时,JSON 不喜欢它。

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, ie it can be nested with child elements with the same name at the same level, different depths etc. Like in the example.而且我没有转换实际的 XML,而是一个类似 XML 结构的对象,即它可以嵌套在同一级别、不同深度等具有相同名称的子元素中。就像在示例中一样。 Thanks though!不过还是谢谢!

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. XML 和 JSON 具有不同的数据模型,并且没有完美的最佳方式将一种转换为另一种。 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).您提到的困难之一是具有多个同名子元素的 XML 元素(例如包含多个<p>元素的<div> )。 At first sight it ertermakes sense to turn the <p> elements into a array.乍一看,将<p>元素转换为数组是有道理的。 But then what do you do if there's a <div> that only has one <p> child?但是如果<div>只有一个<p>孩子,你会怎么做? 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.但是您的问题似乎真的是“请告诉我我应该应用哪些转换规则”,唯一的答案是没有对每个人都适用的转换规则。

import org.json.JSONObject;导入 org.json.JSONObject;

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

or try this或者试试这个

XML.toJSONObject(xml_text).toString(); XML.toJSONObject(xml_text).toString();

either of these should work.其中任何一个都应该有效。

download from here JSON JAR从这里下载JSON JAR

Jackson is a good library to convert XML to JSON in Java. Jackson 是一个很好的库,可以在 Java 中将 XML 转换为 JSON。 Please check this Jackson tutorial.请查看杰克逊教程。

Michael Kay is right.迈克尔·凯是对的。 You need to build your own algo to go through your specific structure to write your specific JSON.您需要构建自己的算法来通过特定结构来编写特定的 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.无论如何,imo,最好使用基于流/事件/令牌的 API 来生成 JSON,而不是任何对象映射器。 For example, using your Element structure and a simple JSON parser/generator https://github.com/anatolygudkov/green-jelly :例如,使用您的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);
    }
}

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...例如,您可以存储已按名称排序和分组的子项...

So to convert it to a JSON string in an iterative way I did a preorder traversal with some modification/adjustments.因此,为了以迭代方式将其转换为 JSON 字符串,我进行了一些修改/调整的预序遍历。

Firstly a created a help class, let's call it Node , that was used for traversing the structures.首先创建了一个帮助类,我们称之为Node ,用于遍历结构。 So each Element would be wrapped in such class.所以每个Element都会被包裹在这样的类中。 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).接下来是使用两个数据结构,一个 Set 和一个 Stack(或 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). Set 用于跟踪访问/遍历的节点,Stack 用于遍历(下一个迭代中要遍历的下一个节点)。

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).然后我把根元素包装成一个Node ,压入栈中,就可以开始前序遍历了(其实我还在开始遍历之前加了一个开{ )。 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. “分组”是指如果有多个同名的子元素,它们会被放入Node中的一个列表中,并且该节点被标记为一个列表。

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.如果当前访问的节点没有被标记为列表的一部分,我将: <name of the node/element> :附加到 JSON 字符串结果。 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, eg: "array":[{...},..,{...}] .这是因为如果它是列表的一部分,则不应为每个节点打印名称,因为名称仅应在 JSON 数组的开头打印一次,例如: "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.如果当前访问的节点不是list ,我将{附加到结果中,并附加字段/属性(如果有)。
  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.).否则,如果当前访问的节点一个列表,我附加[并将其每个分组的子元素包装为Nodes ,将它们标记为列表的一部分,并将它们推送到堆栈中(以这种方式标记它们有助于下一个步骤 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.在每次迭代开始时,我使用 Set 来检查当前(最顶层/窥视)节点是否已经被访问过(参见上面的第 4 步),如果有它只能意味着一件事:是时候关闭并弹出堆栈了. 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.然后,如果下一个节点(peek)没有被访问,这意味着我们正在“横向”移动并且应该将,附加到结果中。 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).例如,必须有一个且只有一个根元素(如在 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!这基本上是一个简单的预序遍历......感谢所有的帮助,当然如果有人有意见,更好的解决方案,或者某些情况下这可能不起作用,我会很高兴听到!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM