JSON:InvalidFormatException:无法从String值

时间:2016-01-02 00:42:00

标签: java json rest jackson

我有这个奇怪的问题,当服务器通过REST收到JSON时,Jackson尝试将String转换为整数:

  

BlockquoSchwerwiegend:MappableContainerException中包含的异常无法映射到响应,重新抛出到HTTP容器   com.fasterxml.jackson.databind.exc.InvalidFormatException:无法在字符串值'之前构造int实例:不是有效的整数值    在[来源:org.apache.catalina.connector.CoyoteInputStream@3916f0; line:1,column:182](通过引用链:com.entities.SectionRelation [" listLinkLabel"] - > java.util.HashSet [0] - > com.entities.LinkLabel [&# 34; LinkLabel的"])       at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:55)       在com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:883)       在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)       在com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeWithObjectId(BeanDeserializerBase.java:1045)       在com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)       at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:240)       在com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:212)       在com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:25)       at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:523)       ...

这是错误应该是的实体:

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

这是服务器发送的JSON示例:

{
    "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.addLink = function(source, target) {

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

我不知道为什么杰克逊试图转换&#34; linkLabel&#34; &#34;前&#34;对于一个整数,当类型定义为String时,即使@JsonFormat也不会改变任何东西。只有&#34;&#34; linkLabel&#34;:1&#34;没有错误,但必须有可能只发送&#34;&#34; linkLabel&#34;:&#34;在&#34;&#34;之前。这对我来说似乎非常简单和简单,因为这是实体的正常表示。

在pom.xml中,Jackson使用2.6.3而GlassFish 4.1是应用程序服务器。

2 个答案:

答案 0 :(得分:2)

你有两个名为&#34; linkLabel&#34;在每个JSON对象中。如果希望标准JSON解析器正确提取它们,则JSON对象中的属性名称必须是唯一的。

将要发生的是,其中一个属性将被JSON解析器忽略(静默)。例如:

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

假设第一个属性是被忽略的属性,那么您的代码将尝试将"after"转换为整数...这将失败。

基本上,您的JSON(语义上)格式错误,您需要纠正生成它的任何内容。

更新 - 我想我已经弄明白为什么杰克逊会生成格式错误的JSON。你有:

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

以及

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

换句话说,你告诉杰克逊1)有一个id属性,类型为int,名称为linkLabel,2)有一个名为linkLabel的属性,其类型为{ {1}}。

杰克逊对这个矛盾的信息感到有些困惑,并假设你已经指定有两个不同的属性叫String ......它们不起作用 1

您还&gt;&gt;出现&lt;&lt;尝试使用linkLabel作为数据字段(具有非整数内容),这也是有问题的 2

解决方案:要么删除linkLabel注释,要么将其更改为使用 distinct 属性名称,并使用正确的Java类型声明相应的Java字段。

1 - 它不能与Jackson解析器一起工作......但你可以使用自定义解析器。因此,杰克逊有一个(脆弱的)理由,而不是将其视为错误。

2 - 杰克逊无法想出这个......

答案 1 :(得分:0)

如果您使用的是Google的端点框架:请确保@Api注释的@Named中指定的任何参数都是唯一的,无论身体的其他任何对象是否传入。如果使用@Named参数“id”并且具有相同参数的对象,则端点将会混淆。

这似乎是Endpoints实现中的一个错误,因为规范应该区分路径中的序列化值和正文中的序列化值:https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#parameter-object

例如,如果你有:

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
    }
}

你会看到你的终端服务器抛出:

  

[INFO] INFO:调用后端方法时发生异常   [INFO] com.google.api.server.spi.response.BadRequestException:com.fasterxml.jackson.databind.exc.InvalidFormatException:无法从String值'EXAMPLEUUID'构造java.lang.Long的实例:不是有效的Long值   [信息]在[来源:N / A; line:-1,column:-1](通过引用链:com.example.package.MyData [“id”])   [INFO] com.google.api.server.spi.request.RestServletRequestParamReader.read(RestServletRequestParamReader.java:128)

Endpoints Framework看到了@Named(“id”)thingId字段,并将其反序列化为MyData,这不符合要求。简单地重新命名参数将解决这个问题:@Named(“thingId”)thingId。