如何解决如何在Java中将类似XML的结构化对象转换为JSON字符串?
因此,我有一个XML结构化对象(Java),并且需要一些帮助将其转换为JSON字符串,而无需使用任何库并且以迭代的方式进行。
例如
<root>
<a/>
<b/>
<a/>
<a>
<c/>
<a/>
<d>
...
</d>
<root/>
哪些可以在类(Java)中表示:
class Element {
String name; (root)
List<Element> childs; (a,b,a,d)(i.e. direct childs)
}
并且应使用JSON:
{
"root":{
"a":[
{},{},{
"c":{}
}
],"b": {},"d": {
...
}
}
}
深度可以是无限的(不是真的,但是可以非常深),并且JSON字符串不需要缩进或保持相同的顺序,只需要示例中有效的JSON。困难的事情(除非使其成为迭代解决方案)是在同一级别具有相同名称的几个元素应为JSON中的数组,因为当存在带有相同元素的元素时JSON不喜欢它在同一级别使用相同的名称。
编辑: 如前所述,我不是在寻找图书馆。此外,你们中许多人提到的库都使用递归,这也不是我想要的。而且我不是在转换实际的XML,而是一个类似于XML结构的对象,即它可以与具有相同名称,相同级别,不同深度等的子元素嵌套在一起。例如在示例中。不过谢谢!
解决方法
我不确定“不使用任何库”是什么意思,也不确定“迭代”或“最佳”是什么意思。
XML和JSON具有不同的数据模型,并且没有完美的最佳方式将它们彼此转换。有许多不同的库可以很好地完成工作,但是它们都有局限性。您提到的困难之一是XML元素具有多个具有相同名称的子元素(例如<div>
包含多个<p>
元素)。乍一看,将<p>
元素转换成数组是有道理的。但是,如果有一个<div>
只有一个<p>
的孩子,您该怎么办?每个转换器都可以找到针对此问题的不同答案,但没有一个是完美的。
说您不想使用任何库意味着您要编写自己的转换器。这不是一个疯狂的主意,因为您可以根据自己的特定数据模型的性质调整转换规则。然后,您可以确定“最佳”对您而言意味着什么。
但是您的问题确实似乎是“请告诉我应该应用哪种转换规则”,唯一的答案是,没有适合所有人的转换规则。
,导入org.json.JSONObject;
JSONObject xmlJsonObj = XML.toJSONObject(new String(buf,“ utf-8”));
或尝试
XML.toJSONObject(xml_text).toString();
这两个都应该起作用。
从此处JSON JAR下载
,Jackson是一个很好的库,可以用Java将XML转换为JSON。 请查看this Jackson教程。
, Michael Kay是对的。您需要构建自己的算法来遍历您的特定结构以编写您的特定JSON。穿过树状结构是一种递归。当然,如果需要,可以将其转换为迭代形式,但是递归似乎更自然。无论如何,imo,最好使用基于流/事件/令牌的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":[{},{}]}]}}}
还要注意,您的算法将取决于您的特定数据/元素结构。例如,您可以存储已经订购并按名称分组的子级,等等...
,因此,以迭代方式将其转换为JSON字符串,我进行了一些修改/调整的预遍历。
首先创建一个帮助类,我们将其称为Node
,该类用于遍历结构。因此,每个Element
都将被包装在此类中。此类帮助我用某些值标记节点,并用相同的名称对元素进行分组,这将在后面详细介绍。
接下来的事情是使用两个数据结构,一个Set和一个Stack(或双端队列)。使用Set来跟踪经过/经过的节点,并使用Stack进行遍历(在下一次迭代中要遍历的下一个节点)。
然后,我将根元素包装为Node
并将其推入堆栈,然后可以开始进行遍历(实际上,在开始遍历之前,我还添加了一个开头{
)。在每次迭代中,子元素都被“分组”并包装为节点,然后被压入堆栈。 “分组”的意思是,如果有多个同名子元素,则将它们放在Node
的列表中,并将该节点标记为列表。
将子元素推入堆栈后,我做了以下事情:
- 如果当前访问的节点未未标记为列表的部分,我将:
<name of the node/element> :
附加到JSON字符串结果中。这是因为如果它是列表的 part ,则不应为每个节点打印出该名称,因为对于JSON中的数组,该名称仅应在开始时打印一次。例如:{{ 1}}。 - 如果当前访问的节点不是列表,则我将
"array":[{...},..,{...}]
附加到结果中,并且还附加字段/属性(如果有)。 - 否则,如果当前访问的节点是一个列表,则我附加了
{
并将其每个分组的子元素包装为[
,将其标记为列表的一部分,并将其推入堆栈(以这种方式标记它们有助于步骤1的下一次迭代。) - 将当前访问的节点添加到集合中。
右括号呢?在每次迭代的开始,我使用Set来检查是否已经访问了当前(最上层/窥视)节点(请参阅上面的第4步),如果它只意味着一件事:是时候关闭并弹出堆栈了。如果该节点被标记为列表,我们将使用Nodes
将其关闭,否则将使用]
将其关闭。然后,如果未未访问下一个节点(窥视),则表示我们正在“侧身”移动,应在结果后附加}
。否则,如果堆栈为空,则附加最后一个结束,
。然后,转到下一个迭代。
我知道这是一个很长的答案,伪代码可能会更好(或者我无法显示的实际代码……是的,它有效!),但是我试图尽我所能。此解决方案可能不适用于所有情况。例如,必须只有一个根元素(如XML)。但是,这为我工作。基本上,这是一个简单的预购遍历...感谢您的所有帮助,当然,如果有人有意见,更好的解决方案,或者某些情况下这可能行不通,我将非常高兴听到!
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。