使用 Jackson 反序列化时禁用标量到字符串的转换
Posted
技术标签:
【中文标题】使用 Jackson 反序列化时禁用标量到字符串的转换【英文标题】:Disable conversion of scalars to strings when deserializing with Jackson 【发布时间】:2019-09-13 20:47:08 【问题描述】:我想识别通过 POST
请求的请求正文发送的 JSON 中不带引号插入的数值(作为字符串):
例如,这将是错误的 JSON 格式,因为年龄字段不包含引号:
"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"
打印到控制台。
【问题讨论】:
提示:查看 JSON 库,例如 Google 的 GSON。 你用的是什么JSON解析库? 这只是一个通过java处理的JSON post请求 反逗号;通常称为引号 【参考方案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。
如果您现在运行您的应用程序,并且您传递了无效的年龄,例如 12
、12.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\"])
【讨论】:
【参考方案2】:默认情况下,当目标字段为String
类型时,Jackson 会将标量值转换为字符串。这个想法是为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;
当一个整数被发送并且需要字符串时,这里是错误:
"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 在 [Source: (PushbackInputStream);行:3,列:13] (通过参考链: org.hello.model.Student[\"age\"])","path":"/hello/echo"
【讨论】:
以上是关于使用 Jackson 反序列化时禁用标量到字符串的转换的主要内容,如果未能解决你的问题,请参考以下文章
使用 Jackson 进行 DateTime 反序列化的默认时区(Joda-Time 模块)
使用 lombok @Builder 时通过 Jackson 反序列化“2021-09-24 00:00:00”日期格式