如何使用 Jackson 将字符串反序列化为自定义对象?

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

我有这个 JSON 我想解析:

{
 "name": "john",
}

我必须使用以下层次结构。 类 是不可变的,我必须通过静态工厂方法访问它们(这是必要的,因此建议对 NamePerson 进行修改是没有意义的)。

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

为此,我想用 Jackson 反序列化一个 Person,所以我这样写:

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);
  }
}

当然,这行不通。它甚至无法编译。

我知道我可以写类似下面的东西,但是 Name 到处都在使用,并不总是在 Person 中,所以我真的需要一个单独的反序列化器来 Name.

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

如何在不求助于中间 POJO 的情况下进行反序列化?

如果你可以修改Person,你可以使用@JsonCreator:

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

这将正确使用 JSON,将“名称”属性 映射到方法的第一个参数。

如果您无法修改 class,您可以使用 mixin 方法,创建 mixin:

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

并在映射器中注册:

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

我必须对 NameDeserializer 使用 parser.getValueAsString(),对 PersonDeserializer 使用 codec.treeToValue():

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);
  }
}