Olivier Grégoire
Olivier Grégoire

Reputation: 35467

How do I deserialize a string as a custom object with Jackson?

I have this JSON that I want to parse:

{
 "name": "john",
}

I must use the following hierarchy. The classes are immutable and I must access them by the static factory method (this is imperative, so it makes no sense to suggest modifications to either Name or Person).

class Name {
  static Name valueOf(String name) {...}
  private Name() {}
  String name();
}
class Person {
  static Person create(Name name) {...}
  private Person() {...}
  Name name();
}

To that end, I want to deserialize a Person with Jackson, so I wrote this:

public class NameJsonDeserializer extends JsonDeserializer<Name> {
  @Override public Name deserialize(JsonParser parser, DeserializationContext context) {
    var tree = parser.getCodec().readTree(parser);
    var name = tree.asToken().asString();
    return Name.valueOf(name);
  }
}
public class PersonJsonDeserializer extends JsonDeserializer<Person> {
  @Override public Person deserialize(JsonParser parser, DeserializationContext context) {
    var tree = parser.getCodec().readTree(parser);
    var name = (ObjectNode) tree.get("name");
    return Person.create(name);
  }
}

But of course, this doesn't work. It doesn't even compile.

I know I can write something similar to the following, but Name is used all over the place, and not always within a Person, so I really need a separate deserializer for Name.

var tree = parser.getCodec().readTree(parser);
var name = (TextNode) tree.get("name");
return Person.create(Name.valueOf(name.asText()));

How can I deserialize without having recourse to intermediary POJOs?

Upvotes: 0

Views: 1837

Answers (2)

Franjavi
Franjavi

Reputation: 657

If you can modify Person, you could use @JsonCreator:

@JsonCreator
public static Person create(@JsonProperty("name") String name) {
    Name nameInstance = Name.valueOf(name);
    return new Person(nameInstance);
}

This will consume that JSON properly, mapping the "name" property to the first param of the method.

If you cannot modify the class you can use the mixin approach, creating the mixin:

interface PersonMixin {
    @JsonCreator
    public static Person create(@JsonProperty("name") Name name) {return null;}
}

and register it in the mapper:

 mapper.addMixIn(Person.class, PersonMixin.class);

Upvotes: 1

Olivier Gr&#233;goire
Olivier Gr&#233;goire

Reputation: 35467

I must use parser.getValueAsString() for NameDeserializer and codec.treeToValue() for PersonDeserializer:

public class NameJsonDeserializer extends JsonDeserializer<Name> {
  @Override public Name deserialize(JsonParser parser, DeserializationContext context) {
    var name = parser.getValueAsString();
    return Name.valueOf(name);
  }
}
public class PersonJsonDeserializer extends JsonDeserializer<Person> {
  @Override public Person deserialize(JsonParser parser, DeserializationContext context) {
    var codec = parser.getCodec();
    var tree = codec.readTree(parser);
    
    var name = codec.treeToValue(tree.get("name"), Name.class);

    return Person.create(name);
  }
}

Upvotes: 2

Related Questions