4

本地化的小数点分隔符,我用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; 

} 
+0

你有什么是无效的JSON。让提供JSON的人为你提供合法的东西。 – 2014-11-05 00:33:19

+0

@HotLicks由于小数点分隔符','是否无效?如果你的意思是后面的逗号 - 这是我忘记输入的内容,我修正了它。 – JJD 2014-11-05 09:40:19

+1

逗号小数点无效JSON。你可以去json.org并查看语法。可能会出现逗号,因为不知何故,您对服务器的请求意味着一个语言环境,并且更改该隐含的语言环境可以解决该问题,但否则,另一端的人应该修复它。 – 2014-11-05 12:03:49

回答

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中找到。它看起来好像我应该使用一种或另一种,但不是两种。无论如何,该文档还声称,当using =定义as =将被忽略:

如果使用()也用于它的优先级(因为它直接指定解串器,而这将仅被用于定位解串器)并且此注释属性的值将被忽略。

+0

有同样的问题,我很惊讶,没有整洁的@JsonFormat(“###,##”)做这个 – psp 2015-03-10 08:19:33

0

在接受答案的所有方面,有一种方法可以去掉这些@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())); 
+0

这是否属于这里? '并将自定义HttpMessageConverter添加到WebMvcConfigurerAdapter消息转换器列表中:' – JJD 2015-05-27 13:32:59

+0

是的,默认情况下,SpringBoot不考虑Jackson2ObjectMapperBuilder Bean。如果你看看默认的WebMvcConfigurerAdapter是如何被初始化的,你会注意到它使用Jackson2ObjectMapperBuilder.json()静态方法而不是Bean注入 – WeMakeSoftware 2015-05-27 13:39:03

2

比其他拟议的答案,这需要更多的通用解决方案注册每种类型的单个解串器,将提供定制的DefaultDeserializationContextObjectMapper

下实现(这是由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将无法​​解析。