Rename ObjectId _id to id in jackson deserializati

2019-01-28 06:14发布

问题:

I've just started working on a project using the play framework,jongo and MongoDB. The project was initially written in Play 2.1 with pojos with an String id field annotated with both: @Id and @ObjectId This would persist to Mongo as an ObjectId and when deserialized would output the id as: "id":"53fcb9ede4b0b18314098d10" for example.

Since upgrading to Jongo 1.1 and Play 2.3.3 the id attribute is always named "_id" when deserialized, I want the attribute to retain the field name yet I can't use @JsonProperty("custom_name") as the Jongo @Id annotation does @JsonProperty("_id") behind the scenes.

import org.jongo.marshall.jackson.oid.Id;
import org.jongo.marshall.jackson.oid.ObjectId;

public class PretendPojo {

    @Id
    @ObjectId
    private String id;

    private String name;

    public PretendPojo() {
    }

    public PretendPojo(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getId() {
        return id;
    }

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

The POJOs when persisted in MongoDB look like this if I view them via RoboMongo

{
    "_id" : ObjectId("53fc984de4b0c34f1905b8ee"),
    "name" : "Owen"
}

However when I deserialize them I get the following json if I keep both annotations:

{"name":"Owen","_id":{"time":1409072858000,"date":1409072858000,"timestamp":1409072858,"new":false,"timeSecond":1409072858,"inc":308487737,"machine":-458223042}}

and the following output if I only use the @Id annotation.

{"name":"Owen","_id":"53fcbedae4b0123e12632639"}

I have a test case for working with the PretendPojo show above:

   @Test
    public void testJongoIdDeserialization() throws UnknownHostException {
        DB database = new MongoClient("localhost", 27017).getDB("jongo");
        Jongo jongo = new Jongo(database);
        MongoCollection collection = jongo.getCollection("jongo");
        collection.save(new PretendPojo("Owen"));
        PretendPojo pretendPojo = collection.findOne("{name:   \"Owen\"}").as(PretendPojo.class);
        JsonNode json = Json.toJson(pretendPojo);
        assertNotNull(json.get("id"));
    }

When trying to use custom deserializers I never can get hold of the object ID I seem to only have access to the date/time/timestamp data that is currently being deserialized.

Ideally the output I'm looking for would be:

  {"name":"Owen","id":"53fcbedae4b0123e12632639"}

Any help will be greatly appreciated! :)

回答1:

ObjectIdSerializer always writes property mapped with @ObjectId to a new instance of ObjectId. This is wrong when you map this property to a String.

To avoid this behaviour, I've write a NoObjectIdSerializer :

public class NoObjectIdSerializer extends JsonSerializer<String> {
    @Override
    public void serialize(String value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        jgen.writeString(value);
    }
}

used like this :

@ObjectId
@JsonSerialize(using = NoObjectIdSerializer.class)
protected final String _id;

There is an open issue.



回答2:

I think that there is an annotation in jackson that allows you to change the property name, I think is : @JsonProperty but you can see all the possible annotations in this link:

https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations

I hope this solve your problem



回答3:

You can try using @JsonValue, Jongo does not seem to use them, but without any response from the developers this behaviour might be subject to change in future releases.

@JsonValue
public Map<String, Object> getJson() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", name);
    map.put("id", id);
    return map;
}

The more proper solution would be to try combining @JsonView with @Id annotation
Remember to specify which View to use on Jongo's ObjectMapper and your Jackson ObjectMapper (the one to use in REST layer, I presume)

@Id
@JsonView(Views.DatabaseView.class)
private String id;

@JsonView(Views.PublicView.class)
public String getId() {
    return id;
}


回答4:

The Jongo's behaviour has changed since 1.1 for a more consistent handling of its owns annotations.

If your '_id' is a String and you want this field to be stored into Mongo as a String then only @Id is needed.

@Id + @ObjectId on a String property means :

"My String property named 'foo' is a valid ObjectId. This property has to be stored with the name '_id' and have to be handled as an ObjectId."