我有一个Java对象。它有许多字段,这些字段引用其他不同类型的Java对象,有时引用本身。该对象可以最好地描述为具有双向引用(或循环)的映射(或图形)。我无权分析它的结构,但为了解决问题,我必须序列化这个图并将其存储在JSON String中。
事实上,我不能真正看到对象的结构,使用库类是我唯一的选择(据我所知)。我试过json-io, json-lib,谷歌genson, gson和flexjson。但是所有这些库要么被卡住,抛出一个异常,因为存在一个循环,或者能够返回一个json(只有json-io这样做),但有很多重要的字段被跳过(那些是惰性加载的,需要getter)。
问题:在测试上面列出的库只是为了解决我的问题时,是否存在一些我可能遗漏的东西?
我的Java对象确实很复杂,但我不希望它像facebook这样的大型网站使用的对象那样复杂。有哪些关键库及其特定配置可用于解决我的问题?
我认为你最好的选择是Jackson Streaming API。它允许您在遍历对象图时将POJO序列化为json,从而保持对序列化过程的控制,并且您可以检测和处理循环引用和任何其他特殊情况。
编辑:我试图实现一个例子,处理循环引用,但我不能完成它。以下是我的中间发现:
- 调用默认的
ObjectMapper.WriteValue(...)
会导致以下异常com.fasterxml.jackson.databind.JsonMappingException: Direct self-reference leading to cycle
,这意味着Jackson可以检测到自引用的情况。默认派生程序不知道如何处理这种情况。 - 默认抛出异常的行为可以通过指定
mapper.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false);
来关闭,默认派生程序将导致堆栈溢出。因此,我们需要实现一个自定义序列化器来检测和处理自引用。 我试着写一个自定义序列化器,做以下事情:A)检测自我引用。B)如果找到了,打印一些东西来标记参考文献。C)如果没有,则执行默认的序列化。然而,我不知道如何调用"默认序列化"。我的中间解决方案如下:
一个可以有self引用的示例类:
public class Node {
public String name = "";
public Node child = null;
public Node(String name) { this.name = name; }
public String getname() { return name; }
public Node getChild() { return child; }
public void setChild(Node n) { child = n; }
}
自定义序列化器
public class NodeJsonSerializer extends JsonSerializer<Node> {
// list so we can detect multiple cases of self references
static List<Node> alreadySerializedNodes = new ArrayList<>();
@Override
public void serialize(Node n, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
for (Node alreadySerialized : alreadySerializedNodes) {
// do not invoke equals() since you want to find duplicate references
if (alreadySerialized == n) {
// mark this as self reference
gen.writeStartObject();
gen.writeStringField("node-ref", alreadySerialized.getname());
gen.writeEndObject();
return;
}
}
alreadySerializedNodes.add(n);
// default derialization ...
gen.writeStartObject();
gen.writeStringField("name", n.getname());
gen.writeObjectField("child", n.getChild());
gen.writeEndObject();
}
}
调用:Node n1 = new Node("n1");
n1.setChild(n1); // self referencing
ObjectMapper mapper = new ObjectMapper();
// registering custom serializer is through module
SimpleModule sm = new SimpleModule("MyModule");
sm.addSerializer(Node.class, new NodeJsonSerializer());
// making sure default serializer ignores self referencing is through module mapper.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false);
mapper.registerModule(sm);
System.out.println(mapper.writeValueAsString(n1));
输出为{"name":"n1","child":{"node-ref":"n1"}}