我使用Jackson解析的输入流包含纬度和经度值,例如:
{
"name": "product 23",
"latitude": "52,48264",
"longitude": "13,31822"
}
由于某种原因,服务器使用逗号作为生成InvalidFormatException
的小数分隔符。由于我无法更改服务器输出格式,因此我想教杰克逊ObjectMapper
处理这些案件。以下是相关代码:
public static Object getProducts(final String inputStream) {
ObjectMapper objectMapper = new ObjectMapper();
try {
return objectMapper.readValue(inputStream,
new TypeReference<Product>() {}
);
} catch (UnrecognizedPropertyException e) {
e.printStackTrace();
} catch (InvalidFormatException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (JsonParseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
这是POJO:
import com.fasterxml.jackson.annotation.JsonProperty;
public class Product {
@JsonProperty("name")
public String name;
@JsonProperty("latitude")
public float latitude;
@JsonProperty("longitude")
public float longitude;
}
我如何告诉Jackson这些坐标值是否带有德语区域设置?
我认为a custom deserializer for the specific fields as discussed here将是最佳选择。我起草了这个:
public class GermanFloatDeserializer extends JsonDeserializer<Float> {
@Override
public Float deserialize(JsonParser parser, DeserializationContext context)
throws IOException {
// TODO Do some comma magic
return floatValue;
}
}
然后POJO看起来像这样:
import com.fasterxml.jackson.annotation.JsonProperty;
public class Product {
@JsonProperty("name")
public String name;
@JsonDeserialize(using = GermanFloatDeserializer.class, as = Float.class)
@JsonProperty("latitude")
public float latitude;
@JsonDeserialize(using = GermanFloatDeserializer.class, as = Float.class)
@JsonProperty("longitude")
public float longitude;
}
答案 0 :(得分:6)
我提出了以下解决方案:
public class FlexibleFloatDeserializer extends JsonDeserializer<Float> {
@Override
public Float deserialize(JsonParser parser, DeserializationContext context)
throws IOException {
String floatString = parser.getText();
if (floatString.contains(",")) {
floatString = floatString.replace(",", ".");
}
return Float.valueOf(floatString);
}
}
...
public class Product {
@JsonProperty("name")
public String name;
@JsonDeserialize(using = FlexibleFloatDeserializer.class)
@JsonProperty("latitude")
public float latitude;
@JsonDeserialize(using = FlexibleFloatDeserializer.class)
@JsonProperty("longitude")
public float longitude;
}
当我将返回值类指定为as = Float.class
时,我仍然想知道为什么我无法工作可以在documentation of JsonDeserialize
中找到。它看起来好像我应该使用其中一个而不是两个。无论如何,文档还声称在定义as =
时将忽略using =
:
如果也使用using(),它具有优先权(因为它直接指定了反序列化器,而这只会用于定位反序列化器),并且忽略此注释属性的值。
答案 1 :(得分:2)
比其他建议的答案更通用的解决方案,需要为每种类型注册单独的反序列化器,是为DefaultDeserializationContext
提供自定义ObjectMapper
。
以下实现(受DefaultDeserializationContext.Impl
启发)对我有用:
class LocalizedDeserializationContext extends DefaultDeserializationContext {
private final NumberFormat format;
public LocalizedDeserializationContext(Locale locale) {
// Passing `BeanDeserializerFactory.instance` because this is what happens at
// 'jackson-databind-2.8.1-sources.jar!/com/fasterxml/jackson/databind/ObjectMapper.java:562'.
this(BeanDeserializerFactory.instance, DecimalFormat.getNumberInstance(locale));
}
private LocalizedDeserializationContext(DeserializerFactory factory, NumberFormat format) {
super(factory, null);
this.format = format;
}
private LocalizedDeserializationContext(DefaultDeserializationContext src, DeserializationConfig config, JsonParser parser, InjectableValues values, NumberFormat format) {
super(src, config, parser, values);
this.format = format;
}
@Override
public DefaultDeserializationContext with(DeserializerFactory factory) {
return new LocalizedDeserializationContext(factory, format);
}
@Override
public DefaultDeserializationContext createInstance(DeserializationConfig config, JsonParser parser, InjectableValues values) {
return new LocalizedDeserializationContext(this, config, parser, values, format);
}
@Override
public Object handleWeirdStringValue(Class<?> targetClass, String value, String msg, Object... msgArgs) throws IOException {
// This method is called when default deserialization fails.
if (targetClass == float.class || targetClass == Float.class) {
return parseNumber(value).floatValue();
}
if (targetClass == double.class || targetClass == Double.class) {
return parseNumber(value).doubleValue();
}
// TODO Handle `targetClass == BigDecimal.class`?
return super.handleWeirdStringValue(targetClass, value, msg, msgArgs);
}
// Is synchronized because `NumberFormat` isn't thread-safe.
private synchronized Number parseNumber(String value) throws IOException {
try {
return format.parse(value);
} catch (ParseException e) {
throw new IOException(e);
}
}
}
现在使用您想要的语言环境设置对象映射器:
Locale locale = Locale.forLanguageTag("da-DK");
ObjectMapper objectMapper = new ObjectMapper(null,
null,
new LocalizedDeserializationContext(locale));
如果您使用Spring RestTemplate
,则可以将其设置为使用objectMapper
,如下所示:
RestTemplate template = new RestTemplate();
template.setMessageConverters(
Collections.singletonList(new MappingJackson2HttpMessageConverter(objectMapper))
);
请注意,该值必须在JSON文档中表示为字符串(即{"number": "2,2"}
),因为例如{"number": 2,2}
无效JSON,无法解析。
答案 2 :(得分:0)
尽管已经接受了答案,但有一种方法可以摆脱那些@JsonDeserialize
注释。
您需要在ObjectMapper中注册自定义反序列化程序。
按照教程from official web-site,您只需执行以下操作:
ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule(
"DoubleCustomDeserializer",
new com.fasterxml.jackson.core.Version(1, 0, 0, null))
.addDeserializer(Double.class, new JsonDeserializer<Double>() {
@Override
public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
String valueAsString = jp.getValueAsString();
if (StringUtils.isEmpty(valueAsString)) {
return null;
}
return Double.parseDouble(valueAsString.replaceAll(",", "\\."));
}
});
mapper.registerModule(testModule);
如果您使用的是Spring Boot,则有一种更简单的方法。只需在Configuration类中的某处定义Jackson2ObjectMapperBuilder bean:
@Bean
public Jackson2ObjectMapperBuilder jacksonBuilder() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.deserializerByType(Double.class, new JsonDeserializer<Double>() {
@Override
public Double deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
String valueAsString = jp.getValueAsString();
if (StringUtils.isEmpty(valueAsString)) {
return null;
}
return Double.parseDouble(valueAsString.replaceAll(",", "\\."));
}
});
builder.applicationContext(applicationContext);
return builder;
}
并将自定义HttpMessageConverter
添加到WebMvcConfigurerAdapter
邮件转换器列表中:
messageConverters.add(new MappingJackson2HttpMessageConverter(jacksonBuilder().build()));