如何指示 Jackson ObjectMapper 不将数字字段值转换为字符串 属性?
How to instruct Jackson ObjectMapper to not convert number field value into a String property?
我有以下 JSON 示例:
{
"channel": "VTEX",
"data": "{}",
"refId": 143433.344,
"description": "teste",
"tags": ["tag1", "tag2"]
}
应该映射到以下 class:
public class AddConfigInput {
public String channel;
public String data;
public String refId;
public String description;
public String[] tags;
public AddConfigInput() {
}
}
使用如下代码:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
String json = STRING_CONTAINING_THE_PREVIOUS_INFORMED_JSON;
AddConfigInput obj = mapper.readValue(json, AddConfigInput.class);
System.out.println(mapper.writeValueAsString(obj));
作为输出产生:
{"channel":"VTEX","data":"{}","refId":"143433.344","description":"teste","tags":["tag1","tag2"]}
请注意字段 refId 是 String 类型,我想避免这种从数字到字符串属性的自动转换.相反,我想 Jackson 抛出一个关于类型不匹配的错误。我该怎么做?
检查它是否适合您。
我为属性 refId 添加了一个自定义反序列化器,我正在检查类型,以防数据类型不匹配并抛出异常。
用于强制数据类型检查的自定义解串器。
KeepStringDeserializer.java
package oct2020.json;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class KeepStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) {
throw deserializationContext.wrongTokenException(jsonParser,
String.class, JsonToken.VALUE_STRING,
"Expected value is string but other datatype found.");
}
return jsonParser.getValueAsString();
}
}
AddConfigInput.java
对于使用自定义解串器的 refId 属性。
package oct2020.json;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
public class AddConfigInput {
public String channel;
public String data;
@JsonDeserialize(using = KeepStringDeserializer.class)
public String refId;
public String description;
public String[] tags;
public AddConfigInput() {
}
}
TestClient.java
package oct2020.json;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestClient {
public static void main(String[] args) throws JsonMappingException,
JsonProcessingException {
String json = "{\n \"channel\": \"VTEX\",\n \"data\": \"{}\",\n \"refId\": 143433.344,\n \"description\": \"teste\",\n \"tags\": [\"tag1\", \"tag2\"]\n}\"";
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
AddConfigInput obj = mapper.readValue(json, AddConfigInput.class);
System.out.println(mapper.writeValueAsString(obj));
}
}
输出:
案例 1:数据不匹配
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT), Expected value is string but other datatype found.
at [Source: (String)"{
"channel": "VTEX",
"data": "{}",
"refId": 143433.344,
"description": "teste",
"tags": ["tag1", "tag2"]
}""; line: 4, column: 14] (through reference chain: oct2020.json.AddConfigInput["refId"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
案例2:正确数据
输入:
String json = "{\n \"channel\": \"VTEX\",\n \"data\": \"{}\",\n \"refId\": \"143433.344\",\n \"description\": \"teste\",\n \"tags\": [\"tag1\", \"tag2\"]\n}\"";
输出:
{"channel":"VTEX","data":"{}","refId":"143433.344","description":"teste","tags":["tag1","tag2"]}
似乎mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
适用于相反的情况,即在将String
值反序列化为数字字段时解析失败。
为 refId
字段提供自定义反序列化程序似乎可以解决此问题。
public class AddConfigInput {
public String channel;
public String data;
//@JsonDeserialize(using = ForceStringDeserializer.class)
public String refId;
public String description;
public String[] tags;
public AddConfigInput() {
}
}
public class ForceStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(
JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException
{
if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) {
deserializationContext.reportWrongTokenException(
String.class, JsonToken.VALUE_STRING,
"Attempted to parse token %s to string",
jsonParser.getCurrentToken());
}
return jsonParser.getValueAsString();
}
}
更新
此自定义反序列化器可以在 ObjectMapper
中注册并覆盖默认行为:
public class ForcedStringParserModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public ForcedStringParserModule() {
this.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(
DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> deserializer)
{
if (String.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new ForceStringDeserializer();
}
return deserializer;
}
});
}
}
那么这个模块可以注册ObjectMapper
:
mapper.registerModule(new ForcedStringParserModule ());
稍微修改输入 JSON 后(对必须为字符串的 data
字段使用布尔值),抛出以下异常:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException:
Unexpected token (VALUE_FALSE), expected VALUE_STRING:
Attempted to parse token VALUE_FALSE to string
at [Source: (String)"{
"channel": "VTEX",
"data": false,
"refId": "143433.344",
"description": "teste",
"tags": ["tag1", "tag2"]
}"; line: 3, column: 13]
我有以下 JSON 示例:
{
"channel": "VTEX",
"data": "{}",
"refId": 143433.344,
"description": "teste",
"tags": ["tag1", "tag2"]
}
应该映射到以下 class:
public class AddConfigInput {
public String channel;
public String data;
public String refId;
public String description;
public String[] tags;
public AddConfigInput() {
}
}
使用如下代码:
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
String json = STRING_CONTAINING_THE_PREVIOUS_INFORMED_JSON;
AddConfigInput obj = mapper.readValue(json, AddConfigInput.class);
System.out.println(mapper.writeValueAsString(obj));
作为输出产生:
{"channel":"VTEX","data":"{}","refId":"143433.344","description":"teste","tags":["tag1","tag2"]}
请注意字段 refId 是 String 类型,我想避免这种从数字到字符串属性的自动转换.相反,我想 Jackson 抛出一个关于类型不匹配的错误。我该怎么做?
检查它是否适合您。
我为属性 refId 添加了一个自定义反序列化器,我正在检查类型,以防数据类型不匹配并抛出异常。
用于强制数据类型检查的自定义解串器。
KeepStringDeserializer.java
package oct2020.json;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
public class KeepStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) {
throw deserializationContext.wrongTokenException(jsonParser,
String.class, JsonToken.VALUE_STRING,
"Expected value is string but other datatype found.");
}
return jsonParser.getValueAsString();
}
}
AddConfigInput.java
对于使用自定义解串器的 refId 属性。
package oct2020.json;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
public class AddConfigInput {
public String channel;
public String data;
@JsonDeserialize(using = KeepStringDeserializer.class)
public String refId;
public String description;
public String[] tags;
public AddConfigInput() {
}
}
TestClient.java
package oct2020.json;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestClient {
public static void main(String[] args) throws JsonMappingException,
JsonProcessingException {
String json = "{\n \"channel\": \"VTEX\",\n \"data\": \"{}\",\n \"refId\": 143433.344,\n \"description\": \"teste\",\n \"tags\": [\"tag1\", \"tag2\"]\n}\"";
ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
AddConfigInput obj = mapper.readValue(json, AddConfigInput.class);
System.out.println(mapper.writeValueAsString(obj));
}
}
输出:
案例 1:数据不匹配
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (VALUE_NUMBER_FLOAT), Expected value is string but other datatype found.
at [Source: (String)"{
"channel": "VTEX",
"data": "{}",
"refId": 143433.344,
"description": "teste",
"tags": ["tag1", "tag2"]
}""; line: 4, column: 14] (through reference chain: oct2020.json.AddConfigInput["refId"])
at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
案例2:正确数据
输入:
String json = "{\n \"channel\": \"VTEX\",\n \"data\": \"{}\",\n \"refId\": \"143433.344\",\n \"description\": \"teste\",\n \"tags\": [\"tag1\", \"tag2\"]\n}\"";
输出:
{"channel":"VTEX","data":"{}","refId":"143433.344","description":"teste","tags":["tag1","tag2"]}
似乎mapper.disable(MapperFeature.ALLOW_COERCION_OF_SCALARS);
适用于相反的情况,即在将String
值反序列化为数字字段时解析失败。
为 refId
字段提供自定义反序列化程序似乎可以解决此问题。
public class AddConfigInput {
public String channel;
public String data;
//@JsonDeserialize(using = ForceStringDeserializer.class)
public String refId;
public String description;
public String[] tags;
public AddConfigInput() {
}
}
public class ForceStringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(
JsonParser jsonParser, DeserializationContext deserializationContext)
throws IOException
{
if (jsonParser.getCurrentToken() != JsonToken.VALUE_STRING) {
deserializationContext.reportWrongTokenException(
String.class, JsonToken.VALUE_STRING,
"Attempted to parse token %s to string",
jsonParser.getCurrentToken());
}
return jsonParser.getValueAsString();
}
}
更新
此自定义反序列化器可以在 ObjectMapper
中注册并覆盖默认行为:
public class ForcedStringParserModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public ForcedStringParserModule() {
this.setDeserializerModifier(new BeanDeserializerModifier() {
@Override
public JsonDeserializer<?> modifyDeserializer(
DeserializationConfig config, BeanDescription beanDesc,
JsonDeserializer<?> deserializer)
{
if (String.class.isAssignableFrom(beanDesc.getBeanClass())) {
return new ForceStringDeserializer();
}
return deserializer;
}
});
}
}
那么这个模块可以注册ObjectMapper
:
mapper.registerModule(new ForcedStringParserModule ());
稍微修改输入 JSON 后(对必须为字符串的 data
字段使用布尔值),抛出以下异常:
Exception in thread "main" com.fasterxml.jackson.databind.exc.MismatchedInputException:
Unexpected token (VALUE_FALSE), expected VALUE_STRING:
Attempted to parse token VALUE_FALSE to string
at [Source: (String)"{
"channel": "VTEX",
"data": false,
"refId": "143433.344",
"description": "teste",
"tags": ["tag1", "tag2"]
}"; line: 3, column: 13]