使用Jackson进行反序列化时,禁止将标量转换为字符串

时间:2019-04-24 14:04:06

标签: java json spring-boot jackson numeric

我想识别通过POST请求的请求正文发送的JSON中插入的不带引号(作为字符串)的数值:

例如,这将是错误的JSON格式,因为age字段不包含引号:

{
  "Student":{
    "Name": "John",
    "Age":  12
  }
}

正确的JSON格式为:

{
  "Student":{ 
    "Name": "John",
    "Age":  "12"
  }
}

在我的代码中,我已经将age字段的数据类型定义为String,因此"12"应该是正确的输入。但是,即使使用12,也不会引发任何错误消息。

似乎Jackson会自动将数值转换为字符串。如何识别数值并返回消息?

这是我到目前为止尝试识别这些数值的方法:

public List<Student> getMultiple(StudentDTO Student) {
    if(Student.getAge().getClass()==String.class) {
        System.out.println("Age entered correctly as String");
    } else{
        System.out.println("Please insert age value inside inverted commas");
    }
}

但是,插入年龄而不带引号的情况下,这不会在控制台上打印"Please insert age value inside inverted commas"

2 个答案:

答案 0 :(得分:1)

默认情况下,当目标字段为String类型时,Jackson会将标量值转换为String。这个想法是为String类型创建一个自定义反序列化器,并注释掉转换部分:

package jackson.deserializer;

import java.io.IOException;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;


public class CustomStringDeserializer extends StringDeserializer 
{

    public final static CustomStringDeserializer instance = new CustomStringDeserializer();

    @Override
    public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        if (p.hasToken(JsonToken.VALUE_STRING)) {
            return p.getText();
        }
        JsonToken t = p.getCurrentToken();
        // [databind#381]
        if (t == JsonToken.START_ARRAY) {
            return _deserializeFromArray(p, ctxt);
        }
        // need to gracefully handle byte[] data, as base64
        if (t == JsonToken.VALUE_EMBEDDED_OBJECT) {
            Object ob = p.getEmbeddedObject();
            if (ob == null) {
                return null;
            }
            if (ob instanceof byte[]) {
                return ctxt.getBase64Variant().encode((byte[]) ob, false);
            }
            // otherwise, try conversion using toString()...
            return ob.toString();
        }
        // allow coercions for other scalar types
        // 17-Jan-2018, tatu: Related to [databind#1853] avoid FIELD_NAME by ensuring it's
        //   "real" scalar
        /*if (t.isScalarValue()) {
            String text = p.getValueAsString();
            if (text != null) {
                return text;
            }
        }*/
        return (String) ctxt.handleUnexpectedToken(_valueClass, p);
    }

}

现在注册该反序列化器:

@Bean
public Module customStringDeserializer() {
    SimpleModule module = new SimpleModule();
    module.addDeserializer(String.class, CustomStringDeserializer.instance);
    return module;
}

当发送整数并且需要String时,出现以下错误:

  

{“ timestamp”:“ 2019-04-24T15:15:58.968 + 0000”,“ status”:400,“ error”:“ Bad   Request“,” message“:” JSON解析错误:无法反序列化以下实例   java.lang.String个(共VALUE_NUMBER_INT个令牌);嵌套异常为   com.fasterxml.jackson.databind.exc.MismatchedInputException:无法   从VALUE_NUMBER_INT中反序列化java.lang.String的实例   令牌\ n位于[来源:(PushbackInputStream);行:3,列:13]   (通过参考链:   org.hello.model.Student [\“ age \”])“”,“路径”:“ / hello / echo”}

答案 1 :(得分:1)

如果您使用的是Spring Boot,则默认情况下它将使用Jackson解析JSON。如this issue中所述,Jackson中没有配置选项可禁用此功能。解决方案是注册自定义JsonDeserializer,一旦遇到JsonToken.VALUE_STRING

以外的任何其他令牌,该自定义就会抛出异常
public class StringOnlyDeserializer extends JsonDeserializer<String> {
    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
        if (!JsonToken.VALUE_STRING.equals(jsonParser.getCurrentToken())) {
            throw deserializationContext.wrongTokenException(jsonParser, String.class, JsonToken.VALUE_STRING, "No type conversion is allowed, string expected");
        } else {
            return jsonParser.getValueAsString();
        }
    }
}

如果仅要将其应用于某些类或字段,则可以使用@JsonDeserialize批注对其进行批注。例如:

public class Student {
    private String name;
    @JsonDeserialize(using = StringOnlyDeserializer.class)
    private String age;

    // TODO: Getters + Setters
}

或者,您可以通过注册SimpleModule bean来注册自定义的Jackson模块,该bean使用StringOnlyDeserializer自动反序列化所有字符串。例如:

@Bean
public Module customModule() {
    SimpleModule customModule = new SimpleModule();
    customModule.addDeserializer(String.class, new StringOnlyDeserializer());
    return customModule;
}

这类似于what Eugen suggested

如果您现在运行应用程序,并且经过了无效的期限,例如1212.3[12],它将抛出异常,并显示以下消息:

JSON parse error: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT), expected VALUE_STRING: Not allowed to parse numbers to string\n at [Source: (PushbackInputStream); line: 3, column: 9] (through reference chain: com.example.xyz.Student[\"age\"])