java 如何使用 Jackson 使用本地化小数点分隔符反序列化浮点值
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26725278/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to deserialize a float value with a localized decimal separator with Hymanson
提问by JJD
The input stream I am parsing with Hymansoncontains latitude and longitude values such as here:
我与Hymanson解析的输入流包含纬度和经度值,例如:
{
"name": "product 23",
"latitude": "52,48264",
"longitude": "13,31822"
}
For some reason the server uses commasas the decimal separator which produces an InvalidFormatException
. Since I cannot change the server output format I would like to teach Hymanson's ObjectMapper
to handle those cases. Here is the relevant code:
出于某种原因,服务器使用逗号作为小数点分隔符,产生InvalidFormatException
. 由于我无法更改服务器输出格式,因此我想教 HymansonObjectMapper
处理这些情况。这是相关的代码:
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;
}
And here is the POJO:
这是 POJO:
import com.fasterxml.Hymanson.annotation.JsonProperty;
public class Product {
@JsonProperty("name")
public String name;
@JsonProperty("latitude")
public float latitude;
@JsonProperty("longitude")
public float longitude;
}
How can I tell Hymanson that those coordinate values come with a German locale?
我怎么能告诉 Hymanson 这些坐标值带有德国语言环境?
I suppose a custom deserializer for the specific fields as discussed herewould be the way to go. I drafted this:
我想这里讨论的特定领域的自定义反序列化器将是要走的路。我起草了这个:
public class GermanFloatDeserializer extends JsonDeserializer<Float> {
@Override
public Float deserialize(JsonParser parser, DeserializationContext context)
throws IOException {
// TODO Do some comma magic
return floatValue;
}
}
Then the POJO would look like this:
然后 POJO 将如下所示:
import com.fasterxml.Hymanson.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;
}
采纳答案by JJD
I came up with the following solution:
我想出了以下解决方案:
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;
}
Still I wonder why I it does not workwhen I specify the return value class as as = Float.class
as can be found in the documentation of JsonDeserialize
. It reads as if I am supposed to use one or the other but not both. Whatsoever, the docs also claim that as =
will be ignored when using =
is defined:
不过我不知道为什么我它不工作的时候我指定的返回值作为类as = Float.class
如能在中找到的文件JsonDeserialize
。它读起来好像我应该使用一个或另一个而不是两个。无论如何,文档还声称as =
在using =
定义时将被忽略:
if using() is also used it has precedence (since it directly specified deserializer, whereas this would only be used to locate the deserializer) and value of this annotation property is ignored.
如果 using() 也被使用,则它具有优先级(因为它直接指定了反序列化器,而这只会用于定位反序列化器)并且忽略此注释属性的值。
回答by Halle Knast
A more general solution than the other proposed answers, which require registering individual deserializers for each type, is to provide a customized DefaultDeserializationContext
to ObjectMapper
.
比其他建议的答案(需要为每种类型注册单独的解串器)更通用的解决方案是提供自定义DefaultDeserializationContext
的ObjectMapper
.
The following implementation (which is inspired by DefaultDeserializationContext.Impl
) worked for me:
以下实现(受 启发DefaultDeserializationContext.Impl
)对我有用:
class LocalizedDeserializationContext extends DefaultDeserializationContext {
private final NumberFormat format;
public LocalizedDeserializationContext(Locale locale) {
// Passing `BeanDeserializerFactory.instance` because this is what happens at
// 'Hymanson-databind-2.8.1-sources.jar!/com/fasterxml/Hymanson/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);
}
}
}
Now set up your object mapper with your desired locale:
现在用你想要的语言环境设置你的对象映射器:
Locale locale = Locale.forLanguageTag("da-DK");
ObjectMapper objectMapper = new ObjectMapper(null,
null,
new LocalizedDeserializationContext(locale));
If you use Spring RestTemplate
, you can set it up to use objectMapper
like so:
如果您使用 Spring RestTemplate
,则可以将其设置为如下使用objectMapper
:
RestTemplate template = new RestTemplate();
template.setMessageConverters(
Collections.singletonList(new MappingHymanson2HttpMessageConverter(objectMapper))
);
Note that the value must be represented as a string in the JSON document (i.e. {"number": "2,2"}
), since e.g. {"number": 2,2}
is not valid JSONand will fail to parse.
请注意,该值必须在 JSON 文档(即{"number": "2,2"}
)中表示为字符串,因为 eg{"number": 2,2}
不是有效的JSON并且将无法解析。
回答by WeMakeSoftware
With all respect to accepted answer, there is a way to get rid of those @JsonDeserialize
annotations.
对于已接受的答案,有一种方法可以摆脱这些@JsonDeserialize
注释。
You need to register the custom deserializer in the ObjectMapper.
您需要在 ObjectMapper 中注册自定义反序列化器。
Following the tutorial from official web-siteyou just do something like:
ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule(
"DoubleCustomDeserializer",
new com.fasterxml.Hymanson.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);
If you're using Spring Boot there is a simpler method. Just define the Hymanson2ObjectMapperBuilder bean somewhere in your Configuration class:
如果您使用的是 Spring Boot,则有一种更简单的方法。只需在 Configuration 类中的某处定义 Hymanson2ObjectMapperBuilder bean:
@Bean
public Hymanson2ObjectMapperBuilder HymansonBuilder() {
Hymanson2ObjectMapperBuilder builder = new Hymanson2ObjectMapperBuilder();
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;
}
and add the custom HttpMessageConverter
to the list of WebMvcConfigurerAdapter
message converters:
并将自定义添加HttpMessageConverter
到WebMvcConfigurerAdapter
消息转换器列表中:
messageConverters.add(new MappingHymanson2HttpMessageConverter(HymansonBuilder().build()));