Gson - 在某些字段上简化序列化

时间:2016-11-29 14:58:25

标签: java json gson

我正在尝试序列化树结构中的复杂对象。例如:

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}]}}

我怎样才能做到这一点?谢谢你的任何想法!

1 个答案:

答案 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,可让您以更低级别的方式解决问题,但我会说这绝对是一种矫枉过正的问题。