Jackson JSON密钥名称映射

时间:2015-11-08 19:53:24

标签: java json mongodb jackson

我开始使用Jackson ObjectMapper从JSON String解析为MongoDB BasicDBObject

  • readValue期间,我希望杰克逊用替代字符替换字段名称中的任何保留字符

  • writeValue期间,我希望杰克逊能够进行反向替换

这样做的原因是Mongo DB不支持.$作为JSON中密钥名称的一部分,我有遗留数据我不太热衷转换,以便我可以坚持它没有特殊的字符映射..

我正在寻找正确的集成点,我可以覆盖readValue期间生成的字段名称,以及将对象序列化回字符串时使用的集成点(writeValue

我已经查看了SimpleModule.addKeySerializerSimpleModule.addKeyDeserializer,并实施了一个简单的映射。但是,这似乎只适用于我的嵌套JSON对象的第一级。任何嵌套对象似乎都没有正确映射其名称。

什么是正确的集成点来覆盖字段名称的映射方式?

1 个答案:

答案 0 :(得分:1)

我应该在前言中提到我不是杰克逊的专家,但我看到两个可能的地方你可以整合这种功能:

  • 自定义序列化程序/反序列化程序
  • 为您进行映射的一系列POJO

第一种方法可能有点沉重,但您可以编写自定义serializerdeserializer (或多个。我在本例中使用了其中一种)处理你的密钥名称中的字符替换。

举个例子,我使用了一些JSON,里面有一些句号和美元符号,如下所示:

{\"username.\":\"testuser\",\"id\":\"12345\",\"$role\":{\"role\":\"admin\",\"roleId$\":\"999\"}}

并将取代"。"与" _"和" $"用" |",然后再回来。我不太确定如何进一步浓缩这个,所以请原谅长篇文章。

使用课程:

    public class User {

    private final String username;
    private final String id;
    private final RoleInfo role;

      public User(String username, String id, RoleInfo role) {
          this.username = username;
          this.id = id;
          this.role = role;
      }

      ...getters omitted
    }

    public class RoleInfo {

      private final String role;
      private final String roleId;

      public RoleInfo(String role, String roleId) {
          this.role = role;
          this.roleId = roleId;
      }
      ...getters omitted
  }

串行器/反序列化器(假设杰克逊的最新版本,您可能希望在本例中更加小心处理节点的方式):

public class MyCustomDeserializer extends JsonDeserializer<User> {

    private final String period;
    private final String dollarSign;

    public MyCustomDeserializer(final String period, final String dollarSign){
        this.period = period;
        this.dollarSign = dollarSign;
    }

    @Override
    public User deserialize(JsonParser p, DeserializationContext ctxt)
            throws IOException, JsonProcessingException {

        final TreeNode node = p.getCodec().readTree(p);

        final TextNode roleNode = (TextNode) node.get(this.dollarSign + "role").get("role");
        final TextNode roleIdNode = (TextNode) node.get(this.dollarSign + "role").get("roleId" + this.dollarSign);
        final String roleName = roleNode.asText();
        final String roleId = roleIdNode.asText();
        final RoleInfo roleInfo = new RoleInfo(roleName, roleId);



        final TextNode usernameNode = (TextNode) node.get("username" + period);
        final TextNode idNode = (TextNode) node.get("id");
        final String username = usernameNode.asText();
        final String userId = idNode.asText();

        final User user = new User(username, userId, roleInfo);

        return user;
    }

}

public class MyCustomSerializer extends JsonSerializer<User> {

    private final String period;
    private final String dollarSign;

    public MyCustomSerializer(final String period, final String dollarSign) {
        this.period = period;
        this.dollarSign = dollarSign;
    }

    @Override
    public void serialize(User user, JsonGenerator gen,
            SerializerProvider serializers) throws IOException,
            JsonProcessingException {

        gen.writeStartObject();
        gen.writeStringField("username" + this.period, user.getUsername());
        gen.writeStringField("id", user.getId());
        gen.writeFieldName(this.dollarSign + "role");
        gen.writeStartObject();
        gen.writeStringField("role", user.getRole().getRole());
        gen.writeStringField("roleId" + this.dollarSign, user.getRole()
                .getRoleId());
        gen.writeEndObject();
        gen.writeEndObject();

    }

}

序列化和反序列化:

final String testJson = "{\"username.\":\"testuser\",\"id\":\"12345\",\"$role\":{\"role\":\"admin\",\"roleId$\":\"999\"}}";

final ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(User.class, new MyCustomDeserializer(".", "$"));
module.addSerializer(User.class, new MyCustomSerializer("_", "|"));
mapper.registerModule(module);

final User user = mapper.readValue(testJson, User.class);
printUser(user);

final String convertedJson = mapper.writeValueAsString(user);
System.out.println(convertedJson);

System.out.println("");


final ObjectMapper mapper2 = new ObjectMapper();
SimpleModule module2 = new SimpleModule();
module2.addDeserializer(User.class, new MyCustomDeserializer("_", "|"));
module2.addSerializer(User.class, new MyCustomSerializer(".", "$"));
mapper2.registerModule(module2);

final User user2 = mapper2.readValue(convertedJson, User.class);
printUser(user2);

final String json2 = mapper2.writeValueAsString(user);
System.out.println(json2);

产地:

12345
testuser
admin
999
{"username_":"testuser","id":"12345","|role":{"role":"admin","roleId|":"999"}}

12345
testuser
admin
999
{"username.":"testuser","id":"12345","$role":{"role":"admin","roleId$":"999"}}

正如我所说,这是非常沉重的,所以你也可以尝试创建一些POJO来为你做映射。我不确定是否有更优雅的方法使用各种json annotations and some POJOs来解决这个问题,但我没有看到在一个POJO中使用两组替换字符进行序列化和反序列化的方法这种方法。链接中的DRCB's approach为我生成了序列化的两个属性。

我想你也可以考虑在各自的注释中支持两组POJOS和每个保留字符,并在你从各种来源读/写时在它们之间进行转换。像(伪代码)的东西:

ClassA{

    @JsonProperty("$user")
    private String user;

}

ClassB{

    @JsonProperty("#user")
    private String user;

}

ClassA a = ClassA.from(classB);

或者您可以组合这两种方法并编写自定义序列化程序,并使用带有多个setter注释的POJO进行反序列化。我认为解决方案将根据您的JSON和POJO的复杂程度而有所不同。