如何反序列化浮点值与杰克逊
本地化的小数点分隔符,我用Jackson分析输入流包含经纬度值,如这里:如何反序列化浮点值与杰克逊
{
"name": "product 23",
"latitude": "52,48264",
"longitude": "13,31822"
}
出于某种原因,服务器使用
逗号作为
产生InvalidFormatException
的小数点分隔符。由于我无法更改服务器输出格式,因此我想教授Jackson的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;
}
我怎么能告诉杰克逊,这些坐标值都配有德文场所?
我想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;
}
我想出了以下解决方案:
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
中找到。它看起来好像我应该使用一种或另一种,但不是两种。无论如何,该文档还声称,当using =
定义as =
将被忽略:
如果使用()也用于它的优先级(因为它直接指定解串器,而这将仅被用于定位解串器)并且此注释属性的值将被忽略。
有同样的问题,我很惊讶,没有整洁的@JsonFormat(“###,##”)做这个 – psp 2015-03-10 08:19:33
在接受答案的所有方面,有一种方法可以去掉这些@JsonDeserialize
注释。
您需要在ObjectMapper中注册自定义的反序列化器。
继教程from official web-site你只是这样做:如果你使用Spring启动还有一个更简单的方法
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);
。只需在您的配置类的地方定义Jackson2ObjectMapperBuilder豆:
@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()));
这是否属于这里? '并将自定义HttpMessageConverter添加到WebMvcConfigurerAdapter消息转换器列表中:' – JJD 2015-05-27 13:32:59
是的,默认情况下,SpringBoot不考虑Jackson2ObjectMapperBuilder Bean。如果你看看默认的WebMvcConfigurerAdapter是如何被初始化的,你会注意到它使用Jackson2ObjectMapperBuilder.json()静态方法而不是Bean注入 – WeMakeSoftware 2015-05-27 13:39:03
比其他拟议的答案,这需要更多的通用解决方案注册每种类型的单个解串器,将提供定制的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 = 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}
无效JSON将无法解析。
你有什么是无效的JSON。让提供JSON的人为你提供合法的东西。 – 2014-11-05 00:33:19
@HotLicks由于小数点分隔符','是否无效?如果你的意思是后面的逗号 - 这是我忘记输入的内容,我修正了它。 – JJD 2014-11-05 09:40:19
逗号小数点无效JSON。你可以去json.org并查看语法。可能会出现逗号,因为不知何故,您对服务器的请求意味着一个语言环境,并且更改该隐含的语言环境可以解决该问题,但否则,另一端的人应该修复它。 – 2014-11-05 12:03:49