簡體   English   中英

MongoDB ObjectId 的 Jackson JSON 反序列化

[英]Jackson JSON Deserialization of MongoDB ObjectId

好的,首先這里是從我的 Web 服務返回的 JSON。 在我的 Android ContentProvider 的 ResponseHandler 中進行異步查詢后,我試圖將其反序列化為 pojo。

{"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 :無法通過引用鏈 [包含所有者類的我的 Jackson bean 的路徑] 在 [char position where you can find "userID" and "paidID" ] 處的START_OBJECT令牌中反序列化java.lang.String實例

我的 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 中的代碼全部失敗(第 2 行產生 JsonMappingException):

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

我有一種感覺,問題是 Jackson 仍然不知道如何映射那些$oid ,它們是 mongoDB ObjectId。 MongoJacksonMapper 庫應該通過提供@ObjectId注釋和一種配置 ObjectMapper 以使用該庫的方法來幫助實現這一點,但它仍然無法正常工作。 出於某種原因,它仍在尋找用戶 ID 或付費 ID 作為字符串,而不是 ObjectId。 有任何想法嗎?

另一種選擇是com.fasterxml.jackson.databind.ser.std.ToStringSerializer

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

這將導致:

{
  "id": "5489f420c8306b6ac8d33897"
}

對於未來的用戶:使用自定義 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);

我的代碼至少有兩個問題很難在網上找到答案,所以我會確保鏈接到這里。 基本上,子類需要在父類中調用 Jackson 的 readValue() 來映射子類的構造函數。 就 mongoDB $oid 而言,您應該創建一個單獨的 MongoId 類來表示這些 mongo 對象,並遵循與子類類似的模式來映射數據,以便進行反序列化。 這是我發現的一篇博客文章,它很好地描述了這一點並提供了一些示例。

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());
 }

}

這里記錄了一種更簡單的方法這對我來說是一個救星。 現在您可以在 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;

我是這樣做的:

@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();
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM