【问题标题】:JSON: InvalidFormatException: Can not construct instance of int from String valueJSON:InvalidFormatException:无法从字符串值构造 int 的实例
【发布时间】:2016-01-02 00:42:00
【问题描述】:

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

BlockquoSchwerwiegend:MappableContainerException 中包含的异常无法映射到响应,重新抛出到 HTTP 容器 com.fasterxml.jackson.databind.exc.InvalidFormatException:无法从字符串值“之前”构造 int 的实例:不是有效的整数值 在 [来源:org.apache.catalina.connector.CoyoteInputStream@3916f0;行:1,列:182](通过引用链:com.entities.SectionRelation["listLinkLabel"]->java.util.HashSet[0]->com.entities.LinkLabel["linkLabel"]) 在 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) 在 com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:289) 在 com.fasterxml.jackson.databind.deser.std.NumberDeserializers$IntegerDeserializer.deserialize(NumberDeserializers.java:271) 在 com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeSetAndReturn(ObjectIdValueProperty.java:85) 在 com.fasterxml.jackson.databind.deser.impl.ObjectIdValueProperty.deserializeAndSet(ObjectIdValueProperty.java:77) 在 com.fasterxml.jackson.databind.deser.impl.BeanPropertyMap.findDeserializeAndSet(BeanPropertyMap.java:285) 在 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) 在 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) 在 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]
        }
}

这是负责前端的sn-p:

this.addLink = function(source, target) {

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

我不明白为什么杰克逊试图将“linkLabel”“之前”转换为整数,当类型定义为字符串时,即使@JsonFormat 也没有改变任何东西。只有 ""linkLabel": 1" 不会引起错误,但必须有可能只发送 ""linkLabel": "before""。这对我来说似乎非常基本和简单,因为这是实体的正常表示。

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

【问题讨论】:

    标签: java json rest jackson


    【解决方案1】:

    在每个 JSON 对象中都有两个名为“linkLabel”的属性。如果您希望标准 JSON 解析器正确提取 JSON 对象中的属性名称,必须是唯一的。

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

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

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

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


    更新 - 我想我已经弄明白了 Jackson 生成格式错误的 JSON 的原因。你有:

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

    还有

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

    换句话说,你告诉 Jackson 1) 有一个类型为 int 和名称为 linkLabel 的 id 属性,2) 有一个名为 linkLabel 的属性,其类型是 String

    Jackson 对这个相互矛盾的信息感到有些困惑,并假设您指定了两个不同的属性,称为 linkLabel ... 这不起作用1

    您还>>出现linkLabel用作数据字段(具有非整数内容),这也是有问题的2

    解决方法:要么去掉@JsonIdentityInfo注解,要么改成使用一个独特的属性名,并用正确的Java类型声明对应的Java字段。


    1 - 它不适用于 Jackson 解析器...但您可以使其与自定义解析器一起使用。因此,杰克逊有一个(微不足道的)理由这样做,而不是将其视为错误。

    2 - 杰克逊无法解决这个问题...

    【讨论】:

    • 第一个附加的 JSON 是由服务器生成的,并且根据几个在线 json 验证器有效。问题是接收和解析不发送。因此包含"listLinkLabel":[{ // "linkLabel": 1 // , "linkLabel": "before" }],表示注释和未注释的解决方案不起作用,只有“”linkLabel“:1”,这没有任何意义
    • @Rooky:JSON 可以读取,但是重复的键会导致很多问题,除非您也想手动编写解析器,否则请避免使用它。您引用的错误消息不足为奇,而且完全合理。 “linkLabel”被确定为对象ID,并被定义为整数。
    • 非常感谢你,你太棒了!我认为这是保存字符的普通 json 标准。
    【解决方案2】:

    如果您使用的是 Google 的 Endpoints Framework:请确保在 @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
        }
    }
    

    你会看到你的 Endpoints 服务器抛出:

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

    Endpoints Framework 看到 @Named("id") thingId 字段并将其反序列化为 MyData,这不符合规范或不期望。只需重命名参数即可解决此问题:@Named("thingId") thingId。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-06-14
      • 1970-01-01
      • 2014-07-20
      • 1970-01-01
      • 1970-01-01
      • 2019-01-21
      • 2018-02-02
      相关资源
      最近更新 更多