잭슨

4

와 지역화 된 소수 구분 기호로 부동 소수점 값을 역 직렬화하는 방법 내가 Jackson 구문 분석하고 입력 스트림은 여기에 같은 위도와 경도 값이 포함되어 서버가 쉼표 등을 사용하는 몇 가지 이유를 들어잭슨

{ 
    "name": "product 23", 
    "latitude": "52,48264", 
    "longitude": "13,31822" 
} 

을 소수점의 단락 기호는 InvalidFormatException가됩니다. 서버 출력 형식을 변경할 수 없으므로 Jackson의 ObjectMapper에 이러한 사례를 처리하도록 알려 드리고 싶습니다. 그 좌표 값이 독일어 로케일로 올 것을

import com.fasterxml.jackson.annotation.JsonProperty; 

public class Product { 

    @JsonProperty("name") 
    public String name; 
    @JsonProperty("latitude") 
    public float latitude; 
    @JsonProperty("longitude") 
    public float longitude; 

} 

는 어떻게 잭슨을 알 수 있습니다 : 여기에 관련 코드입니다 :

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입니까?


나는 a custom deserializer for the specific fields as discussed here 가야 할 길입니다. 나는 다음과 같은 솔루션을 함께했다

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을 제공하는 사람에게 법적 자료를 제공하도록하십시오. –

+0

@HotLicks 소수점 구분 기호로','로 인해 유효하지 않습니까? 당신이 뒤에 오는 콤마를 의미하는 경우에 - 그것은 내가 입력하는 것을 잊었던 것이 었습니다. 나는 그것을 고쳤습니다. – JJD

+1

쉼표 소수점은 유효한 JSON이 아닙니다. json.org에서 구문을 볼 수 있습니다. 어떤 이유로 든 서버에 대한 요청이 로캘을 의미하고 묵시적 로케일을 변경하면 문제가 해결 될 수 있기 때문에 쉼표가 표시 될 수 있습니다. 그렇지 않으면 다른 쪽 사람들이 해결해야합니다. –

답변

6

:

public class GermanFloatDeserializer extends JsonDeserializer<Float> { 

    @Override 
    public Float deserialize(JsonParser parser, DeserializationContext context) 
      throws IOException { 
     // TODO Do some comma magic 
     return floatValue; 
    } 

} 

그런 다음 POJO는 다음과 같이 보일 것이다 :이 초안 ...

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; 

} 

아직도 내가 왜 그런지 궁금하다 documentation of JsonDeserialize에서 찾을 수있는 바와 같이 반환 값 클래스를 as = Float.class으로 지정할 때이 작동하지 않습니다. 그것은 내가 하나 또는 다른 하나를 사용하기로되어 있지만 둘 다 사용하지 않는 것으로 읽습니다. 어떠한 상기 문서는 using = 정의 할 때 as = 무시 될 것임을 항 :

경우() 또한 우선 순위를 가지고 사용하여 (직접적 지정된 이후 디시리얼라이저 이것은 단지 디시리얼라이저의 위치를 ​​찾을 수있는 반면) 이 주석 property의 값은 무시됩니다.

+0

같은 문제가 있습니다.이 작업을 수행 할 때 깔끔한 @JsonFormat ("###, ##")이 필요합니다. – psp

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); 

합니다.

@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; 
} 

WebMvcConfigurerAdapter 메시지 컨버터의 목록에 사용자 정의 HttpMessageConverter을 추가합니다 : 그냥 구성 클래스에 어딘가에 Jackson2ObjectMapperBuilder 빈을 정의 할 필요 다른 제안 된 답변보다

messageConverters.add(new MappingJackson2HttpMessageConverter(jacksonBuilder().build())); 
+0

여기에 속해 있니? '그리고 사용자 정의 HttpMessageConverter를 WebMvcConfigurerAdapter 메시지 변환기 목록에 추가하십시오. ' – JJD

+0

예, 기본적으로 SpringBoot는 Jackson2ObjectMapperBuilder Bean을 고려하지 않습니다. 기본 WebMvcConfigurerAdapter가 초기화되는 방법을 살펴보면 Bean 삽입 대신 Jackson2ObjectMapperBuilder.json() 정적 메서드를 사용한다는 점에 유의하십시오. – WeMakeSoftware

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); 
     } 
    } 
} 

를 설정 : 당신은 봄 RestTemplate 사용하는 경우

Locale locale = Locale.forLanguageTag("da-DK"); 
ObjectMapper objectMapper = new ObjectMapper(null, 
              null, 
              new LocalizedDeserializationContext(locale)); 

, 당신을 다음과 같이 objectMapper을 사용하도록 설정할 수 있습니다.

RestTemplate template = new RestTemplate(); 
template.setMessageConverters(
    Collections.singletonList(new MappingJackson2HttpMessageConverter(objectMapper)) 
); 

값은 JSON 문서에서 문자열로 표시되어야합니다 (예 : {"number": "2,2"}). {"number": 2,2}은 유효하지 않으므로 JSON이며 구문 분석에 실패합니다.