Reputation: 35467
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
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
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