【问题标题】:Jackson JSON Deserialization of MongoDB ObjectIdMongoDB ObjectId 的 Jackson JSON 反序列化
【发布时间】:2012-12-23 17:07:27
【问题描述】:

好的,首先这是从我的 Web 服务返回的 JSON。在我的 Android ContentProvider 的 ResponseHandler 中进行异步查询后,我试图将其反序列化为 pojos。

{"exampleList" : [{
"locationId" : "00001" , 
"owners" : [ 
  { 
    "paidID" : { "$oid" : "50a9c951300493f64fbffdb6"} , 
    "userID" : { "$oid" : "50a9c951300493f64fbffdb6"}
  } , 
  { 
    "paidID" : { "$oid" : "50a9c951300493f64fbffdb7"} , 
    "userID" : { "$oid" : "50a9c951300493f64fbffdb7"}
  } 
]
}]}

起初,我对所看到的问题感到困惑,因为我在 Web 服务中使用了与我在 Android 应用程序中相同的 Jackson 注释 bean,但后来我意识到 owners 对象从来没有在示例 JSON 中发送到我的 Web 服务(它跳过我的 Web 服务上的 POJO,并通过 DAO 的原子更新添加到 mongoDB 中的文档中)。

没关系。到目前为止,Jackson 不必处理 owners 对象,现在它被它窒息了,即:

JsonMappingException: 无法反序列化 java.lang.String 的实例 START_OBJECT 令牌位于 [char 位置,您可以在其中找到 "userID""paidID"] 通过引用链[包含所有者类的我的 Jackson bean 的路径]

我的 Jackson bean 有一个包装器,这就是“exampleList”的全部内容:

public class Examples extends HashMap<String, ArrayList<Example>> {

}

然后是实际的Example 类:

@JsonIgnoreProperties(ignoreUnknown = true)
public class Example implements Comparable<Example> {

@ObjectId @Id
private String id;

@JsonProperty(Constants.Example.location)
private String location;

@JsonProperty(Constants.Example.OWNERS)
private List<Owners> owners;

public int compareTo(Example _o) {
    return getId().compareTo(_o.getId());
}

public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

public String getLocation() {
    return location;
}

public void setLocation(String location) {
    this.location = location;
}
public List<Example.Owners> getOwners() {
    return owners;
}

public void setOwners(List<Example.Owners> owners) {
    this.owners = owners;
}

public Example() {
}

@JsonCreator
public Example(@Id @ObjectId String id) {
    this.id = id;
}

@JsonIgnoreProperties(ignoreUnknown = true)
public static class Owners implements Comparable<Owners> {

    @JsonProperty(Constants.Example.USERID)
    private String userID;

    @JsonProperty(Constants.Example.PAIDID)
    private String paidID;

    public Owners() {
    }

    public int compareTo(Owners _o) {
        return getUserID().compareTo(_o.getUserID());
    }

    @ObjectId
    public String getUserID() {
        return userID;
    }

    @ObjectId
    public void setUserID(String userID) {
        this.userID = userID;
    }

    @ObjectId
    public String getPaidID() {
        return paidID;
    }

    @ObjectId
    public void setPaidID(String paidID) {
        this.paidID = paidID;
    }

}

}

最后,ResponseHandler 中的代码全部失败(第二行产生 JsonMappingException):

objectMapper = MongoJacksonMapperModule.configure(objectMapper);    
mExamples = objectMapper.readValue(jsonParser, Examples.class);

我感觉问题是 Jackson 仍然不知道如何映射那些 $oid,它们是 mongoDB ObjectId。 MongoJacksonMapper 库应该通过提供 @ObjectId 注释和配置 ObjectMapper 以使用该库的方法来帮助实现这一点,但它仍然无法正常工作。出于某种原因,它仍在寻找作为字符串的 userID 或paidID,而不是 ObjectId。有什么想法吗?

【问题讨论】:

    标签: json jackson mongo-jackson-mapper


    【解决方案1】:

    另一种选择是 com.fasterxml.jackson.databind.ser.std.ToStringSerializer.

    @Id
    @JsonSerialize(using = ToStringSerializer.class)
    private final ObjectId id;
    

    这将导致:

    {
      "id": "5489f420c8306b6ac8d33897"
    }
    

    【讨论】:

    • 问题是关于反序列化,而不是序列化。这个答案应该被否决。
    • @Arny 这可能是完全正确的,但这是一个真正的救星(这在互联网上很难找到)所以我不仅感谢wbiller,而且我也支持它:) 谢谢!
    • 就我而言,在 Kotlin 中做起来太容易了:@JsonSerialize(using = ToStringSerializer::class)
    【解决方案2】:

    对于未来的用户:使用自定义 jackson 反序列化器将 $oid 转换回 ObjectId。

    public class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {
    
        @Override
        public ObjectId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            JsonNode oid = ((JsonNode)p.readValueAsTree()).get("$oid");
            return new ObjectId(oid.asText());
        }
    
    }
    

    使用方法:

    ObjectMapper mapper = new ObjectMapper();
    SimpleModule mod = new SimpleModule("ObjectId", new Version(1, 0, 0, null, null, null));
            
    mod.addDeserializer(ObjectId.class, new ObjectIdDeserializer());
    mapper.registerModule(mod);
    
    YourClass obj = mapper.readValue("{your json with $oid}", YourClass.class);
    

    【讨论】:

    • 也可以只用@JsonDeserialize(using = ObjectIdDeserializer.class)注释字段
    • addDeserializer()方法从何而来?
    【解决方案3】:

    我的代码至少有两个问题很难在网上找到答案,所以我会确保在此处链接。基本上,子类需要在父类中调用 Jackson 的 readValue() 来映射子类的构造函数。就 mongoDB $oid 而言,您应该创建一个单独的 MongoId 类来表示这些 mongo 对象,并遵循与子类类似的模式来映射数据以进行反序列化。 Here's a blog post 我发现这很好地描述了这一点并提供了一些示例。

    【讨论】:

      【解决方案4】:

      Jackson 不知道如何序列化 ObjectId。我调整了 Arny 的代码以序列化任何 ObjectId 并提供这个工作示例:

      public class SerialiserTest {
      
       private ObjectMapper mapper = new ObjectMapper();
      
       public static class T {
          private ObjectId objectId;
      
          public ObjectId getObjectId() {
              return objectId;
          }
      
          public void setObjectId(ObjectId objectId) {
              this.objectId = objectId;
          }
       }
      
       @Test
       public final void serDeser() throws IOException {
          T t = new T();
          t.setObjectId(new ObjectId());
          List<T> ls = Collections.singletonList(t);
          String json = mapper.writeValueAsString(ls);
          System.out.println(json);
          SimpleModule mod = new SimpleModule("ObjectId", new Version(1, 0, 0, null, null, null));
          mod.addDeserializer(ObjectId.class, new ObjectIdDeserializer());
          mapper.registerModule(mod);
          JavaType type = mapper.getTypeFactory().
                  constructCollectionType(List.class, T.class);
          List<?> l  = mapper.readValue(json, type);
          System.out.println(l);
       }
      }
      
      public class ObjectIdDeserializer extends JsonDeserializer<ObjectId> {
      
       @Override
       public ObjectId deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
          JsonNode n = (JsonNode)p.readValueAsTree();
          return new ObjectId(n.get("timestamp").asInt(), n.get("machineIdentifier").asInt(), (short) n.get("processIdentifier").asInt(), n.get("counter").asInt());
       }
      
      }
      

      【讨论】:

        【解决方案5】:

        还有一个更简单的方法documented here,这对我来说是救命稻草。现在您可以在 Java 中使用 ObjectId,但是当您访问/来自 JSON 时,它将是一个字符串。

        public class ObjectIdJsonSerializer extends JsonSerializer<ObjectId> {
            @Override
            public void serialize(ObjectId o, JsonGenerator j, SerializerProvider s) throws IOException, JsonProcessingException {
                if(o == null) {
                    j.writeNull();
                } else {
                    j.writeString(o.toString());
                }
            }
        }
        

        然后在你的豆子里:

        @JsonSerialize(using=ObjectIdJsonSerializer.class)
        private ObjectId id;
        

        【讨论】:

          【解决方案6】:

          我是这样做的:

          @Configuration
          public class SpringWebFluxConfig {
          
              @Bean
              @Primary
              ObjectMapper objectMapper() {
                Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
                builder.serializerByType(ObjectId.class, new ToStringSerializer());
                builder.deserializerByType(ObjectId.class, new JsonDeserializer() {
                  @Override
                  public Object deserialize(JsonParser p, DeserializationContext ctxt) 
                      throws IOException {
                    Map oid = p.readValueAs(Map.class);
                    return new ObjectId(
                        (Integer) oid.get("timestamp"),
                        (Integer) oid.get("machineIdentifier"),
                        ((Integer) oid.get("processIdentifier")).shortValue(),
                        (Integer) oid.get("counter"));
                  }
                });
                return builder.build();
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2013-01-01
            • 2023-03-04
            • 2013-01-17
            • 2015-02-28
            • 2012-01-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多