JJD JJD - 1 year ago 172
Java Question

How to deserialize a float value with a localized decimal separator with Jackson

The input stream I am parsing with Jackson contains latitude and longitude values such as here:

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


For some reason the server uses commas as the decimal separator which produces an
InvalidFormatException
. Since I cannot change the server output format I would like to teach Jackson's
ObjectMapper
to handle those cases. Here is the relevant code:

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:

import com.fasterxml.jackson.annotation.JsonProperty;

public class Product {

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

}


How can I tell Jackson that those coordinate values come with a German locale?




I suppose a custom deserializer for the specific fields as discussed here would 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:

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;

}

JJD JJD
Answer Source

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 work when 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:

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download