简体   繁体   中英

Jackson jsonidentityinfo deserialization fails

I am building a RESTful service to view server relationships (A Server can contain another server as its parent). The service accepts JSON strings for CRUD commands.

I use @JsonIdentityInfo and @JsonIdentityReference in my Server Object, so that the user receives simplified JSON answers like this:

{"hostname":"childhostname", "parent":"parenthostname"}

As parent I only get the hostname of the parent and not a parent object - this is exactly what I want and works fine.

My problem begins when trying to de-serialize an update command (when trying to update the parent). If I send this:

curl -i -X POST -H 'Content-Type: application/json' -d '{"parent":"parenthostname"}' http://localhost:8080/myRestService/rest/servers/childhostname

Nothing happens - the parent will not be set. The problem lies in the delivered JSON string:

{"parent":"parenthostname"}

After debugging hibernate 2.4.4 source code, I found that my JSON string generates a com.fasterxml.jackson.databind.deser.UnresolvedForwardReference: Could not resolve Object Id [parenthostname] . This Exception is not thrown but null will be returned.

When I remove @JsonIdentityInfo and @JsonIdentityReference , this JSON string just works fine and my parent will be updated (but then I lose my simplified answers and also get infinite loop problems).

So if I adjust my JSON string to this:

'{"parent":{"hostname":"parenthostname"}}'

The update works fine. But I would like to have the simplified (unwrapped) version working. Any ideas? I am thankful for any hint.

I am using Hibernate 4.2.4 and Jackson 2.4.4

This is my (simplified) Server class:

@JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="hostname") 
public class Server extends A_Hardware {

@NaturalId
@Column(name="hostname", nullable=false, unique=true)
private String hostname = null;

@ManyToOne
@JsonIdentityReference(alwaysAsId = true)
private Server parent = null;

@OneToMany(fetch = FetchType.LAZY, mappedBy="parent")
@JsonIdentityReference(alwaysAsId = true)
private Set<Server> childServers = new HashSet<Server>();

[...]
// standard getters and setters

This is my RESTful service's update class:

    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.TEXT_PLAIN)
    @Path("{hostname}")
    public Response update(@PathParam("hostname") final String hostname, JsonParser json){
        Server s = null;
        ObjectMapper mapper = new ObjectMapper();
        mapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        try{
            s = mapper.readValue(json, Server.class);

This is my first question here, so please don't judge me too hard if my question might is not completely clear ;)

I kinda solved it with a workaround. To deliver and receive my desired, simplified JSON string, I now use @JsonSetter and @JsonProperty .

Also see this answer .

/**
 * JSON Helper method, used by jackson. Makes it possible to add a parent by just delivering the hostname, no need for the whole object.
 * 
 * This setter enables:
 * {"parent":"hostnameOfParent"}
 * 
 * no need for this:
 * {"parent":{"hostname":"hostnameOfParent"}}
 */
@JsonSetter
private void setParentHostname(String hostname) {
    if(hostname!=null){
        this.parent = new Server(hostname);         
    } else {
        this.parent = null;
    }
}

/**
 * Used by jackson to deserialize a parent only with its hostname
 * 
 * With this getter, only the hostname of the parent is being returned and not the whole parent object
 */
@JsonProperty("parent")
private String getParentHostname(){
    if(parent!=null){
        return parent.getHostname();
    } else {
        return null;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM