2

I have this weird problem that when the server receives a JSON via REST, Jackson tries to convert a String into an Integer:

BlockquoSchwerwiegend: The exception contained within MappableContainerException could not be mapped to a response, re-throwing to the HTTP container com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of int from String value 'before': not a valid Integer value at [Source: org.apache.catalina.connector.CoyoteInputStream@3916f0; line: 1, column: 182] (through reference chain: com.entities.SectionRelation["listLinkLabel"]->java.util.HashSet[0]->com.entities.LinkLabel["linkLabel"]) at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:55) at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:883) at com.fasterxml.jackson.databind.deser.std.StdDeserializer._parseInteger(StdDeserializer.java:411) at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:289) at com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:271) at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:85) at com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:77) at com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.findDeserializeAndSet(BeanPropertyMap.java:285) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:335) at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1045) at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:240) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:212) at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:25) at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:523) ...

This is the entity where the error is supposed to be:

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;

/**
 * The label name is unique and is therefore the 
 * primary key.
 */
@Entity
@JsonIdentityInfo(
        generator = ObjectIdGenerators.IntSequenceGenerator.class,
        property = "linkLabel")
public class LinkLabel implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    @Column(name = "LinkLabel")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String linkLabel;

    @JsonIgnore
    @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
    @JoinTable(
       name="LINKLABEL_LINKSET",
       joinColumns={@JoinColumn(name="LINKLABEL_ID", referencedColumnName="LinkLabel")},
       inverseJoinColumns={@JoinColumn(name="LINK_LABEL_SET_ID", referencedColumnName="id")})
    private Set<LinkSet> linkSet = new HashSet();


    public String getLinkLabel() {
       return linkLabel;
    }

    public void setLinkLabel(String linkLabel) {
       this.linkLabel = linkLabel;
    }

    public Set<LinkSet> getLinkSet() {
       return linkSet;
    }

    public void addLinkSet(LinkSet linkSet) {
       this.linkSet.add(linkSet);
    }
}

This is an example JSON which was sent by the server:

{
    "links": [{
            "id": 2,
            "section1": {
                ...
            },
            "section2": {
                ...
            },
            "listLinkLabel": [{
                    "linkLabel": 1,
                    "linkLabel": "after"
                }]
        }, {
            "id": 5,
            "section1": {
                ...
            },
            "section2": {
                ...
            },
            "listLinkLabel": [{
                    "linkLabel": 2,
                    "linkLabel": "before"
                }, {
                    "linkLabel": 3,
                    "linkLabel": "overlap"
                }, 1]
        }, {
            "id": 3,
            "section1": {
                ...
            },
            "section2": {
                ...
            },
            "listLinkLabel": [3]
        }
}

This is the responsible snippet of the frontend:

this.addLink = function(source, target) {

               var JSONTemplate = {
                    "id":null,
                    "section1":{
                        ...
                    },
                    "section2":{
                        ...
                    },
                    "listLinkLabel":[{
//                            "linkLabel": 1
//                            ,
                            "linkLabel": "before"
                    }]
                };
                $http.post('service/sectionrelation', JSON.stringify(JSONTemplate));
}

I don't see why Jackson tries to convert "linkLabel" "before" to an Integer, when the type is definetely a String, even @JsonFormat doesn't change anything. Only ""linkLabel": 1" evokes no errors , but there must be a possibility to send only ""linkLabel": "before"". This seems pretty basic and simple to me, because this is the normal representation of the entity.

In the pom.xml Jackson is used 2.6.3 and GlassFish 4.1 is the application server.

0

2 Answers 2

2

You have two attributes called "linkLabel" in each of the JSON objects. Attribute names in a JSON object have to be unique if you want them to be extracted correctly by a standard JSON parser.

What is going to happen is that one of the attributes is going to be ignored (silently) by the JSON parser. For example:

    "listLinkLabel": [{
            "linkLabel": 1,
            "linkLabel": "after"
    }]

Assuming that the first attribute is the one that is ignored, your code will then try to convert "after" to an integer ... which will fail.

Basically, your JSON is (semantically) malformed, and you need to correct whatever is generating it.


UPDATE - I think I have figured out why Jackson is generating malformed JSON. You have:

@JsonIdentityInfo(
    generator = ObjectIdGenerators.IntSequenceGenerator.class,
    property = "linkLabel")

and also

@Column(name = "LinkLabel")
@GeneratedValue(strategy = GenerationType.AUTO)
private String linkLabel;

In other words, you have told Jackson that 1) there is a id attribute with type int and name linkLabel, 2) there is a property named linkLabel whose type is String.

Jackson has gotten a bit confused by this contradictory information, and assumed that you have specified there are two distinct attributes called linkLabel ... which doesn't work1.

You also >>appear<< to be trying to use linkLabel as a data field (with non-integer content) and that is also problematic2.

Solution: either get rid of the @JsonIdentityInfo annotation, or change it to use a distinct property name, and declare the corresponding Java field with the correct Java type.


1 - It doesn't work with the Jackson parser ... but you could make it work with a custom parser. Hence, there is a (tenuous) justification for Jackson to do this rather than treating this as an error.

2 - There is no way that Jackson could figure this one out ...

Sign up to request clarification or add additional context in comments.

3 Comments

The first attached JSON is generated by the server and according to several online json validators valid. The problem is the receiving and parsing not sending. Therefore "listLinkLabel":[{ // "linkLabel": 1 // , "linkLabel": "before" }] was included, to indicate that the commented and uncommented solution doesn't work, only " "linkLabel": 1", which does not make any sense
@Rooky: the JSON can be read, but having duplicate keys is going to cause lots of problems, avoid it unless you want to hand-code your parser as well. The error message you cite is no surprise, and totally sensible. "linkLabel" is determined to be the object ID, and is defined to be an integer.
Thank you very much, you are awesome! I thought this is normal json standard to save characters.
0

If you're using Google's Endpoints Framework: Be sure that any parameters specified in your @Api annotation's @Named are unique regardless of any other objects passed in as part of the body. If you use the @Named parameter "id" and also have an object with the same parameter, Endpoints will get confused.

This appears to be a bug in the Endpoints implementation as the specification should differentiate between serialized values in the path and those in the body: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameter-object

For example, if you have:

class MyData {
    private Long id;
    public Long getId(){return id;}
    public void setId(Long v){id = v;}
}


@Api( /* Your API stuff here */ )
class EndpointBase {

    @ApiMethod(
            name = "thingGet",
            httpMethod = "GET")
    public KnockerResponse thingGet(
            @Named("id") String thingId,
            MyData data
    )  {
        // Never executes
    }
}

You'll see your Endpoints server throw:

[INFO] INFO: exception occurred while calling backend method [INFO] com.google.api.server.spi.response.BadRequestException: com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.lang.Long from String value 'EXAMPLEUUID': not a valid Long value [INFO] at [Source: N/A; line: -1, column: -1] (through reference chain: com.example.package.MyData["id"]) [INFO] at com.google.api.server.spi.request.RestServletRequestParamReader.read(RestServletRequestParamReader.java:128)

Endpoints Framework sees the @Named("id") thingId field and is de-serializing it into MyData, which is not per-spec or desired. Simply re-naming the parameter will fix this: @Named("thingId") thingId.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.