7

I am getting data from an external JSON API and parsing the result with Jackson. Unfortunately, that API returns all fields as String and those are filled with "N/A" when data is unavailable.

I would like to replace those fields with null, especially as those are frustrating when I try to convert JSON String fields to more informative Java fields.

A custom DeserializationProblemHandler worked for Integer fields (see below), but it was useless for Java 8's LocalDate fields. Furthermore, it reacts to a problem rather than anticipating it.

I was unable to find a pre-processor to configure into my ObjectMapper and am uneasy with the idea of overriding the BeanDeserializer.

Do you know of a better/cleaner solution to handle this kind of situations? Thanks!


DeserializationProblemHandler

new DeserializationProblemHandler() {
    @Override
    public Object handleWeirdStringValue(DeserializationContext ctxt, Class<?> targetType, String valueToConvert, String failureMsg) throws IOException {
        return "N/A".equals(valueToConvert) ? null : super.handleWeirdStringValue(ctxt, targetType, valueToConvert, failureMsg);
    }
}

Error message when processing "N/A" in LocalDate field

Can not deserialize value of type java.time.LocalDate from String "N/A": Text 'N/A' could not be parsed at index 0

(works fine when there is date in the data)

Chop
  • 4,267
  • 5
  • 26
  • 58
  • 1
    Jackson doesn't generally concern itself with handling specific values of JSON fields. Text based preprocessing may very well be the simplest and most efficient solution for this: `String filteredJson = jsonString.replace("\"N/A\"", "null");`. Otherwise consider catching/handling the Exception. – Manos Nikolaidis May 15 '17 at 13:37
  • I actually thought of it. Just didn't find a way to implement it in my Spring Boot application. All I can do is configure my ObjectMapper. – Chop May 15 '17 at 21:00

1 Answers1

1

I feel like there ought to be a better way of doing this, but the following is the only solution I was able to come up with.

Create a new JsonDeserializer that handles "N/A" input. The following example handles strings:

public class EmptyStringDeserializer extends StdScalarDeserializer<String> {
    public EmptyStringDeserializer() {
        super(String.class);
    }

    @Override
    public String deserialize(JsonParser parser, DeserializationContext ctx) throws IOException {
        final String val = parser.getValueAsString();

        if ("N/A".equalsIgnoreCase(val))
            return null;

        return val;
    }
}

The class is registered with an ObjectMapper like this:

SimpleModule simpleModule = new SimpleModule().addDeserializer(String.class, new EmptyStringDeserializer());
ObjectMapper om = new ObjectMapper().registerModule(simpleModule);

You'll probably want to collect all your converters in a module named for the API that is making you handle things this way.

Henrik Aasted Sørensen
  • 6,966
  • 11
  • 51
  • 60
  • Yup, thought of it, went this way. What held my hand is that it prevents me from using Jackson default Deserializers. I have to reimplement all of them. I even tried to make a proxy Deserializer (if "N/A", return null; else call a deserializer), but I don't know which Deserializer Jackson uses for some of my fields. – Chop May 15 '17 at 21:02