1

Given json:

{
  "name" : {}
}

I would like to parse it into the following Java object using com.fasterxml.jackson:

class MyClass {
  private String name;
}

If you try it, you will get:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token

Is there any way to configure the jackson deserializer (globally for best) it can handle these empty objects as nulls without changing the attribute type (from String)?

angel
  • 77
  • 10
  • 6
    Your json shouldn't be { "name" : null } ? We don't represend null object with {} in json. https://stackoverflow.com/questions/21120999/representing-null-in-json – Gweltaz Niquel Jan 30 '19 at 09:17
  • [Here](https://github.com/FasterXML/jackson-databind/issues/1885) they mention it doesn't support parsing empty objects. One idea from the thread is to just *`replace("{}", "null")` on the JSON string before parsing* which is **not** pretty, but might work. – bkis Jan 30 '19 at 09:22
  • Should be, but that JSON is not mine. And what I have understood, it is still valid with `{}`, right? – angel Jan 30 '19 at 09:22
  • Well, you should always be able to use a custom deserializer to convert empty objects to strings. However, using different types for empty and non-empty values is a receipe for catastrophe, better use `"name":null` as the others suggested - or maybe `"name":""`. Having to check the type is bound to bite you in the a** eventually. – Thomas Jan 30 '19 at 09:24
  • @angel What does the `name` contain if it is not empty? `{}` would make sense if the name consists of multiple parts (like `{firstname: .., lastname:..}`. But in that case you should not deserialize it to a string, but to an object that contains those properties. If it only contains a string but `{}` when it is empty, then it doesn't make sense. – Ivar Jan 30 '19 at 09:27
  • 1
    Right guys, I understand that `name: {}` for `String` is bad. But as I have stated, I'm not producing this kind of messy JSON :-). – angel Jan 30 '19 at 09:29

3 Answers3

0

To do that you can write your own deserializer. Following this you extend the StdDeserializer and register it in the class.


Author edit:

I have used following deserializer for strings and it's working fine, thanks:

public class EmptyObjectDeserializer extends StdDeserializer<String> {

    public EmptyObjectDeserializer() {
        this(null);
    }

    public EmptyObjectDeserializer(Class<?> vc) {
        super(vc);
    }

    @Override
    public String deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        JsonNode node = jp.getCodec().readTree(jp);
        return node.asText("");
    }
}
angel
  • 77
  • 10
Benedikt
  • 68
  • 4
  • 1
    This doesn't help at all. This doesn't describes what the problem is and the correct solution for it. – CodeMatrix Jan 30 '19 at 09:29
  • Will try it for `String.class`. Thanks for suggestion. – angel Jan 30 '19 at 09:29
  • 1
    this may pass as a comment, not as an answer. – Sharon Ben Asher Jan 30 '19 at 09:42
  • 1
    I have edited the answer to include my custom deserializer for string. That solution is working well and is best among the options what I have found. Edit is waiting for review. – angel Jan 30 '19 at 09:47
  • A bit overkill, it could be simplier, no need for a deserializer, just add a class for Name – Julien Revault d'A... Jan 30 '19 at 10:00
  • @JulienRevaultd'A... I don't agree. Why you would want custom class into your model class when there should be a string. And when that model is made up of dozens of attributes and nested class, you really don't want substitute every string into custom class. One serializer is imho much better. – angel Jan 30 '19 at 10:04
  • Just because in JSON as soon as you have a {} it is not a string but an object. And it works perfectly with an object – Julien Revault d'A... Jan 30 '19 at 10:09
0

In json, {} is an Object, meaning it can have fields (like a POJO in Java). As you are trying to deserialise it into a String, it is throwing an Exception (Imagine trying to assign a POJO to a String reference in Java).

In this case, there are two options:

  • Configure your class not to include empty fields. This can be done via annotation or configuration on your ObjectMapper instance, e.g.:

    @JsonInclude(Include.NON_NULL)
    class MyClass {
      private String name;
    }
    

    OR

    mapper.setSerializationInclusion(Include.NON_EMPTY);

    This option might not work if the response has not null name in it. If it's an object with not null value, Jackson will try to deserialise it into a String and fail.

  • Change the type of name to Object (or rather, a POJO with all the elements response can contain). This would however mean you need to change the code that accesses name element as it will no longer be String.

Pshemo
  • 122,468
  • 25
  • 185
  • 269
Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
  • First suggestion with `@JsonInclude` is only for the serialization - that's not what I'm asking. Second suggestion is something I have in mind but would not like to do. THanks – angel Jan 30 '19 at 09:34
  • If your request contains fields under `name` object then you have no other option but to change the type of `name` field if you want to deserialise the response content. – Darshan Mehta Jan 30 '19 at 09:44
0

No need to write your own deserializer, you just have to encapsulate your name object in a class and it works :

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "name"
})
public class JsonExample {
        @JsonProperty("name")
        public Name name;
}

and

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({
        "first",
        "last"
})
public class Name {
    @JsonProperty("first")
    public String first_name;
    @JsonProperty("last")
    public String last_name;
}

first name and last name are here as an example... don't know what's inside your Name object so you can use your mapper :

JsonExample obj = mapper.readValue(file, JsonExample.class);