java JSON Jackson - 使用自定义序列化程序序列化多态类时的异常

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27876027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 12:33:56  来源:igfitidea点击:

JSON Hymanson - exception when serializing a polymorphic class with custom serializer

javajsonHymanson

提问by Humppak?r?j?t

I'm currently migrating some code from Hymanson 1.x to Hymanson 2.5 json mapper and came a long a problem that wasn't there in 1.x.

我目前正在将一些代码从 Hymanson 1.x 迁移到 Hymanson 2.5 json 映射器,但遇到了一个 1.x 中没有的问题。

This is the setup (see code below):

这是设置(见下面的代码):

  • interface IPet
  • class Dog implements IPet
  • IPet is annotated with @JsonTypeInfo and @JsonSubTypes
  • class Human has a property of type IPet that is annotated with @JsonSerialize(using=CustomPetSerializer.class)
  • 接口IPet
  • 类 Dog 实现了 IPe
  • IPet 使用 @JsonTypeInfo 和 @JsonSubTypes 进行注释
  • 类 Human 有一个 IPe 类型的属性,该属性用 @JsonSerialize(using=CustomPetSerializer.class) 注释

The problem:If I serialize an instance of Dog it works as expected (also the type info is added to the json string by Hymanson). However when I serialize an instance of the Human class an exception is thrown saying:

问题:如果我序列化 Dog 的一个实例,它会按预期工作(Hymanson 还将类型信息添加到 json 字符串中)。但是,当我序列化 Human 类的一个实例时,会抛出异常说:

com.fasterxml.Hymanson.databind.JsonMappingException: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])

com.fasterxml.Hymanson.databind.JsonMappingException:未为类型 com.pet.Dog 实现类型 id 处理(通过参考链:com.Human["pet"])

The serialize(...) method of the CustomPetSerializer class is not invoked (tested using a breakpoint).

不会调用 CustomPetSerializer 类的 serialize(...) 方法(使用断点测试)。

The code:

代码:

IPet implementation:

IPet实现:

@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
@JsonSubTypes({
     @JsonSubTypes.Type(value=Dog.class,    name="dog")
    //,@JsonSubTypes.Type(value=Cat.class,  name="cat")
    //more subtypes here...
})
public interface IPet
{
    public Long getId();
    public String getPetMakes();
}

Dog implementation:

狗实现:

public class Dog implements IPet
{
    @Override
    public String getPetMakes()
    {
        return "Wuff!";
    }

    @Override
    public Long getId()
    {
        return 777L;
    }
}

Human who owns a dog:

养狗的人:

public class Human
{
    private IPet pet = new Dog();

    @JsonSerialize(using=CustomPetSerializer.class)
    public IPet getPet()
    {
        return pet;
    }
}

CustomPetSerializer implementation:

CustomPetSerializer 实现:

public class CustomPetSerializer extends JsonSerializer<IPet>
{
    @Override
    public void serialize(IPet value, JsonGenerator gen, SerializerProvider serializers) throws IOException, JsonProcessingException
    {
        if(value != null && value.getId() != null)
        {
            Map<String,Object> style = new HashMap<String,Object>();
            style.put("age", "7");
            gen.writeObject(style);
        }
    }
}

JUnit test method:

JUnit测试方法:

@Test
public void testPet() throws JsonProcessingException
{
    ObjectMapper mapper = new ObjectMapper();

    Human human = new Human();

    //works as expcected
    String json = mapper.writeValueAsString(human.getPet());
    Assert.assertNotNull(json);
    Assert.assertTrue(json.equals("{\"type\":\"dog\",\"id\":777,\"petMakes\":\"Wuff!\"}"));

    //throws exception: Type id handling not implemented for type com.pet.Dog (through reference chain: com.Human["pet"])
    json = mapper.writeValueAsString(human);    //exception is thrown here
    Assert.assertNotNull(json);
    Assert.assertTrue(json.contains("\"age\":\"7\""));
}

回答by edi

You'll need to additionally override serializeWithTypewithin you CustomPetSerializerbecause IPetis polymorphic. That's also the reason why serializeis not called. Check this related SO questionthat explains in detail when serializeWithTypeis called. For instance, your serializeWithTypeimplementation might look something like this:

由于是多态的serializeWithType,您还需要在您内部进行额外覆盖。这也是不叫的原因。检查这个相关的 SO 问题,它详细解释了何时被调用。例如,您的实现可能如下所示:CustomPetSerializerIPetserializeserializeWithTypeserializeWithType

@Override
public void serializeWithType(IPet value, JsonGenerator gen, 
        SerializerProvider provider, TypeSerializer typeSer) 
        throws IOException, JsonProcessingException {

  typeSer.writeTypePrefixForObject(value, gen);
  serialize(value, gen, provider); // call your customized serialize method
  typeSer.writeTypeSuffixForObject(value, gen);
}

which will print {"pet":{"type":"dog":{"age":"7"}}}for your Humaninstance.

这将为{"pet":{"type":"dog":{"age":"7"}}}您的Human实例打印。

回答by dansoton

Since Hymanson 2.9 writeTypePrefixForObject()and writeTypeSuffixForObject()have been deprecated (I'm unclear why). It seems under the new approach it would now be:

由于Hyman逊2.9writeTypePrefixForObject()writeTypeSuffixForObject()已过时(我不清楚为什么)。在新方法下,它现在似乎是:

@Override
public void serializeWithType(IPet value, JsonGenerator gen, 
        SerializerProvider provider, TypeSerializer typeSer) 
        throws IOException, JsonProcessingException {

  WritableTypeId typeId = typeSerializer.typeId(value, START_OBJECT);
  typeSer.writeTypePrefix(gen, typeId);
  serialize(value, gen, provider); // call your customized serialize method
  typeSer.writeTypeSuffix(gen, typeId);
}

So an extra line now, so not sure why it's a step forward, perhaps it's more efficient reusing the typeIdobject.

所以现在多了一行,所以不确定为什么它向前迈出了一步,也许重用typeId对象更有效。

Source:Hymanson's ObjectNodeclass currently in master. Not the best source but couldn't see any upgrade docs explaining what to do.

来源:Hymanson 的ObjectNode类目前在 master 中。不是最好的来源,但看不到任何升级文档来解释要做什么。