用于通用树的自定义Jackson序列化程序

时间:2013-01-28 04:55:05

标签: java json generics jackson

假设我有一个用Java实现的参数化树,如下所示:

public class Tree<E> {
   private static class Node {
      E element;
      List<Node> children.
   }

   Node root;

   //... You get the idea.
}

这里的想法是上面的实现仅涉及树的拓扑结构,但是对实例化将存储在树中的元素一无所知。

现在,说我希望我的树元素成为地理位置。它们在树木中组织的原因是因为大陆包含国家,国家包含州或省,等等。为简单起见,地理位置具有名称和类型:

public class GeoElement { String name; String type; }

最后,地理层次结构如此:

public class Geography extends Tree<GeoElement> {}

现在到杰克逊序列化。假设Jackson序列化程序可以看到字段,则此实现的直接序列化将如下所示:

{
   "root": {
      "element": {
         "name":"Latin America",
         "type":"Continent"
      }
      "children": [
          {
             "element": {
                "name":"Brazil",
                "type":"Country"
             },
             "children": [
                 // ... A list of states in Brazil
             ]
          },
          {
             "element": {
                "name":"Argentina",
                "type":"Country"
             },
             "children": [
                 // ... A list of states in Argentina
             ]
          }
      ]
   }

这种JSON渲染并不好,因为它包含来自Tree和Node类的不必要的工件,即“root”和“element”。我需要的是:

{
   "name":"Latin America",
   "type":"Continent"
   "children": [
       {
          "name":"Brazil",
          "type":"Country"
          "children": [
             // ... A list of states in Brazil
          ]
       },
       {
          "name":"Argentina",
          "type":"Country"
          "children": [
             // ... A list of states in Argentina
          ]
       }
   ]
}

非常感谢任何帮助。 -Igor。

4 个答案:

答案 0 :(得分:7)

您需要的是@JsonUnwrapped

  

用于指示属性应“ unwrapped”序列化的注释;也就是说,如果将其序列化为JSON对象,则其属性将作为其包含对象的属性包含在内

将此注释添加到root类的Treeelement字段的Node字段中,如下所示:

public class Tree<E> {
   private static class Node {

      @JsonUnwrapped
      E element;
      List<Node> children.
   }

   @JsonUnwrapped
   Node root;

   //... You get the idea.
}

它将为您提供所需的输出:

{
    "name": "Latin America",
    "type": "Continent",
    "children": [{
        "name": "Brazil",
        "type": "Country",
        "children": []
    }, {
        "name": "Argentina",
        "type": "Country",
        "children": []
    }]
}

答案 1 :(得分:4)

也许像这样使用@JsonValue

public class Tree<E> {
  @JsonValue
  Node root;
}

如果你只需要“打开”你的树吗?

答案 2 :(得分:0)

您最好的选择是为您的对象构建并注册一个自定义序列化程序。

定义序列化器:

public class NodeSerializer extends StdSerializer<Node> {

然后在您的Node类上:

@JsonSerialize(using = NodeSerializer.class)
public class Node {
}

NodeSerializer

内部
@Override
    public void serialize(
      Node node, JsonGenerator jgen, SerializerProvider provider) 
      throws IOException, JsonProcessingException {

        jgen.writeStartObject();
        jgen.writeStringField("name", node.element.name);

        jgen.writeStringField("type", node.element.type);
        //Process children
        serializeFields(node, jgen, provider);
        jgen.writeEndObject();
    }

此通用框架将让您控制元素的序列化方式。您可能还需要在@JsonIgnore内部的element对象Node,因为您的自定义序列化程序会负责将这些信息推送到生成的JSON中。在线上有很多关于自定义序列化程序和覆盖默认JSON导出的信息。

对于Tree的实现,您可以使用序列化器以类似的方式摆脱根。

如果您不想在类上注册序列化程序,也可以使用ObjectMapper一次一次进行:

ObjectMapper mapper = new ObjectMapper();

SimpleModule module = new SimpleModule();
module.addSerializer(Node.class, new NodeSerializer());
mapper.registerModule(module);

String serialized = mapper.writeValueAsString(tree);

注释方法将在全球范围内应用。这种方法可以控制自定义序列化器的使用方式/位置。

答案 3 :(得分:-2)

要删除element类型,一种可能是更改结构,以便名称和类型将直接包含在每个节点中:

public class TreeGeo {
   private static class Node {
      String name;
      String type;
      List<Node> children.
   }

   Node root;
}

要删除root类型,我不知道。我想你可以从jsonObject中提取一个子对象,但我对Jackson不太了解。但是,你可以给它一个更好的名字,比如world,或者操纵结果字符串,用一些字符串操作手动删除它。