我正在尝试序列化树结构中的复杂对象。例如:
class Tree{
private Node1 rootNode;
private ArrayList<Node> allNodes;
}
class Node1 extends Node{
private String id;
private Node2 node2;
}
class Node2 extends Node{
private String id;
...
}
字段rootNode应该只包含JSON中的rootNodeId。不过,我需要在我的代码中使用实际的Node1对象。 Node1中的Node2属性也是如此。这个想法只是在那里保存一个引用,因为完整的对象将在allNodes数组中。 JSON应该看起来像这样:
{Tree: {rootNodeId: 1, allNodes: [{id: 1, node2Id: 2}, {id: 2}]}}
我怎样才能做到这一点?谢谢你的任何想法!
答案 0 :(得分:1)
在序列化和反序列化中经常使用Data Transfer Object模式。它还有很多优点,比如在不同的应用程序层解耦同一逻辑对象的各种形式,特定序列化库的精确配置(如果有一天你想切换到Jackson?)等等。
假设您的值对象(与DTO相反)具有构造函数,请先构建对象模型(我在这里使用Java 6,之后不要混淆您):
private static Tree createTree() {
final Tree tree = new Tree();
tree.rootNode = new Node1("1");
tree.allNodes = new ArrayList<Node>();
tree.allNodes.add(new Node1("1", new Node2("2")));
tree.allNodes.add(new Node2("2"));
return tree;
}
然后,只需定义您的DTO类,以符合您对GSON的要求:
private static final class RootDto {
@SerializedName("Tree")
@SuppressWarnings("unused")
private final TreeDto tree;
private RootDto(final TreeDto tree) {
this.tree = tree;
}
}
private static final class TreeDto {
@SerializedName("rootNodeId")
@SuppressWarnings("unused")
private final String rootNodeId;
@SerializedName("allNodes")
@SuppressWarnings("unused")
private final List<NodeDto> allNodes;
private TreeDto(final String rootNodeId, final List<NodeDto> allNodes) {
this.rootNodeId = rootNodeId;
this.allNodes = allNodes;
}
}
private abstract static class NodeDto {
}
private static final class Node1Dto
extends NodeDto {
@SerializedName("id")
@SuppressWarnings("unused")
private final String id;
@SerializedName("node2Id")
@SuppressWarnings("unused")
private final String node2Id;
private Node1Dto(final String id, final String node2Id) {
this.id = id;
this.node2Id = node2Id;
}
}
private static final class Node2Dto
extends NodeDto {
@SerializedName("id")
@SuppressWarnings("unused")
private final String id;
private Node2Dto(final String id) {
this.id = id;
}
}
请注意,我在这里使用静态最终类(在一个外部类中声明)只是为了简化演示。另外,我使用@SerializedName
显式定义JSON字段的名称(它允许重命名Java中的字段而不触及JSON字段),以及@SuppressWarnings
以禁止显示字段的警告编译器已知它们是未使用的,因为无论如何它们都在结果JSON中使用。自定义DTO的另一个选项是使用嵌套的java.util.Map
,但我相信基于类的DTO可以为您提供更多的类型安全性。现在只需将value对象转换为其数据传输表示:
private static RootDto toDto(final Tree tree) {
final List<NodeDto> allNodes = new ArrayList<>();
for ( final Node node : tree.allNodes ) {
final NodeDto nodeDto;
if ( node instanceof Node1 ) {
final Node1 node1 = (Node1) node;
nodeDto = new Node1Dto(node1.id, node1.node2.id);
} else if ( node instanceof Node2 ) {
final Node2 node2 = (Node2) node;
nodeDto = new Node2Dto(node2.id);
} else {
throw new AssertionError("must never happen: " + node);
}
allNodes.add(nodeDto);
}
final TreeDto treeDto = new TreeDto(tree.rootNode.id, allNodes);
return new RootDto(treeDto);
}
并将它们构建在一起:
final Gson gson = new Gson();
final Tree tree = createTree();
out.println(gson.toJson(tree));
out.println(gson.toJson(toDto(tree)));
输出如下:
{&#34;根节点&#34; {&#34; ID&#34;:&#34; 1&#34;}&#34; allnodes中&#34;:[{&#34; ID&# 34;:&#34; 1&#34;&#34;节点2&#34; {&#34; ID&#34;:&#34; 2&#34;}},{&#34; ID&#34 ;:&#34; 2&#34;}]}
{&#34;树&#34; {&#34; rootNodeId&#34;:&#34; 1&#34;&#34; allnodes中&#34;:[{&#34; ID&#34;:& #34; 1&#34;,&#34; node2Id&#34;:&#34; 2&#34;},{&#34; id&#34;:&#34; 2&#34;}]}} < / p>
GSON还支持自定义serializers,可让您以更低级别的方式解决问题,但我会说这绝对是一种矫枉过正的问题。