当前位置: 首页 > 知识库问答 >
问题:

如何在Jackson中为用于支持多态反序列化的鉴别器属性添加别名?

上官高翰
2023-03-14

我有一个标准的多态类型<code>Shape</code>,我可以使用标准的<code>@JsonTypeInfo</code>机制对其进行多态反序列化:

import static org.assertj.core.api.Assertions.assertThat;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;

public class DiscriminatorAliasTest {

  @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type")
  @JsonSubTypes({
      @JsonSubTypes.Type(value = Square.class, name = "square"),
      @JsonSubTypes.Type(value = Circle.class, name = "circle")
  })
  static abstract class Shape {
    abstract public String name();
  }

  static class Square extends Shape {
    @JsonProperty("A")
    public float width;

    @Override
    public String name() { return "square"; }
  }

  static class Circle extends Shape {
    @JsonProperty("A")
    public float diameter;

    @Override
    public String name() { return "circle"; }
  }

  @Test
  public void testDiscriminator() throws Exception {
    ObjectMapper mapper = new ObjectMapper();

    // This passes! 
    String squareJson = "{\"type\":\"square\", \"A\": 1.0 }";
    Shape square = mapper.readerFor(Shape.class).readValue(squareJson);
    assertThat(square.name()).isEqualTo("square");
  }
}

但是,我想添加<code>作为鉴别器属性类型的别名,以便以下JSON字符串由Shapeabstract类反序列化:

{ “类型”: “正方形”, “A”: 1.0 }AND { “?”: “square”, “A”: 1.0 }

这样这个测试案例就会通过

java prettyprint-override">  @Test
  public void testDiscriminator() throws Exception {
    ObjectMapper mapper = new ObjectMapper();

    // This passes!
    String squareJson = "{\"type\":\"square\", \"A\": 1.0 }";
    Shape square = mapper.readerFor(Shape.class).readValue(squareJson);
    assertThat(square.name()).isEqualTo("square");

    // and this should pass as well, with NO further modification
    // to this test case
    String squareJsonWithAlternativePropertyName = "{\"?\":\"square\", \"A\": 1.0 }";
    Shape alsoSquare = mapper.readerFor(Shape.class).readValue(squareJsonWithAlternativePropertyName);
    assertThat(alsoSquare.name()).isEqualTo("square");
  }

我为什么要这样做?:我试图将两个不同的现有可区分联合编码方案反序列化到同一个类层次结构中 - 不幸的是,没有办法事先知道类必须反序列化哪个方案。

限制:

    < li >我无法使用< code>@JsonTypeInfo(use = Id。扣除)。必须有一个鉴别器属性,因为属性键集不能用于区分每个类。 < li >在映射之前,我无法访问或操作< code>ObjectMapper,因为此模式向外部模块公开了反序列化实现的详细信息。我假设这可能会排除基于< code > module . setdeserialirmodifier /< code > delegating deserializer /< code > bean deserialirmodifier 的解决方案,例如本问题和本问题中建议的解决方案。 < li >在JSON或等效的< code>JsonNode结构命中< code>ObjectMapper之前,我无法对其进行操作 < li >我不想通过析构< code>JsonNode手动构造< code>Shape实例。我应该合理地期望利用现有的反序列化机制和< code>Shape及其子类型的注释。

我假设这些都是合理的限制——在Scala的Circe JSON库中为鉴别器属性设置别名,同时遵守这些限制相对来说只是一行更改。

提前感谢!

共有2个答案

邵兴文
2023-03-14

没有jackson内置机制可以帮助您实现目标,解决方法是将json字符串转换为ObjectNode,检查<code>“?”属性存在于其中,如果存在,请将“type”属性添加为“?”关联值,删除“?”属性,并使用如下所示的ObjectMapper#treeToValue方法:

String squareJsonWithAlternativePropertyName = "{\"?\":\"square\", \"A\": 1.0 }";
ObjectNode node = (ObjectNode) mapper.readTree(squareJsonWithAlternativePropertyName);
if (node.has("?")) {
   node.set("type", node.get("?"));
   node.remove("?");
}
Shape shape = mapper.treeToValue(node, Shape.class);

您可以将此代码封装在一个函数中,在嵌套属性或其他情况下,代码必须更新,但基本代码基本上保持不变。

党俊健
2023-03-14

这里有一个方法可以解决你的问题。其思想是定义一个自定义反序列化程序,并将其与基类< code>Shape相关联,但恢复继承类的默认反序列化程序(通过一个空的< code>@JsonDeserialize批注)。这个技巧可以防止无限循环。

反序列化器的工作方式如下:

    < li >它将JSON转换为树结构 < li >它从树中提取类型 < li >它使用树和类型调用< code>treeToValue()
import java.io.IOException;
import java.util.HashMap;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.node.*;

public class Test
{
    @JsonDeserialize(using = ShapeDeserializer.class)
    public static abstract class Shape
    {
        abstract public String name();
    }

    @JsonDeserialize
    public static class Square extends Shape
    {
        @JsonProperty("A")
        public float width;

        @Override
        public String name() { return "square"; }
    }

    @JsonDeserialize
    public static class Circle extends Shape
    {
        @JsonProperty("A")
        public float diameter;

        @Override
        public String name() { return "circle"; }
    }

    static class ShapeDeserializer extends JsonDeserializer<Shape>
    {
        private HashMap<String, Class<? extends Shape>> types = new HashMap<>();

        public ShapeDeserializer()
        {
            types.put("square", Square.class);
            types.put("circle", Circle.class);
        }

        public Shape deserialize(JsonParser p, DeserializationContext ctxt) throws IOException
        {
            // Get the tree and extract the type from it
            ObjectNode root = (ObjectNode)p.readValueAsTree();
            TextNode type = (TextNode)root.remove("type");
            if(type==null)
                type = (TextNode)root.remove("?");
            Class<? extends Shape> valueType = types.get(type.textValue());

            // Convert the tree to an instance of the specified class
            return p.getCodec().treeToValue(root, valueType);
        }
    }

    public static void main(String[] args) throws JsonProcessingException
    {
        String squareJson = "{\"type\":\"square\", \"A\": 1.0 }";
        String circleJson = "{\"?\":\"circle\", \"A\": 1.0 }";

        ObjectMapper mapper = new ObjectMapper();
        Shape square = mapper.readerFor(Shape.class).readValue(squareJson);
        Shape circle = mapper.readerFor(Shape.class).readValue(circleJson);

        System.out.println(square.name());
        System.out.println(circle.name());
    }
}

输出:

square
circle
 类似资料:
  • 我的JSON字符串是: 我想要实现的是,当JSON中没有提供schemaVersion时,能够在默认情况下反序列化到SubClassV1,但即使在Superclass中将schemaVersion初始化为“1.0”时,我仍然会收到以下错误:

  • 我们有以下Json: 我们需要根据type属性将这些项以多态方式反序列化为不同的子类。 在自定义Jackson反序列化程序中是否可以获取类型值? 我们可以使用提供给反序列化方法的JsonParser安全地回溯Json树吗? 我发现了这篇关于多态反序列化的博客,但它似乎需要在项目本身上设置一个type属性。 谢谢

  • 我对Jackson和类型层次结构有以下问题。我正在序列化一个类SubA,该类将扩展为一个字符串,然后尝试将其反序列化回来。当然,在编译时,系统不知道它是基还是SubA,因此我希望它是基,如果它是SubA,则会在编译后执行一些其他操作。 我的基本类看起来像: ...和一个派生自的类: ... 我试图执行以下代码: String是: 但我一次又一次地犯同样的错误。映射器现在知道如何处理另一个类参数-它

  • 如果我有一个像这样的类结构: 我宁愿不必强制API的客户端包含来反序列化子类。 不使用,Jackson是否提供了一种方法来注释子类并通过唯一的属性将其与其他子类区分开来?在上面的示例中,这将类似于“如果一个JSON对象具有反序列化它为,如果它具有反序列化它为”。

  • 引用OpenAPI 2.0模式对象或Swagger 2.0模式对象,并将字段定义为: 增加了对多态性的支持。鉴别器是用于区分继承此架构的其他架构的架构属性名称。使用的属性名称必须在此架构中定义,并且必须位于属性列表中。使用时,该值必须是此架构或继承它的任何架构的名称。 我的困惑/问题: 对我来说,它究竟在继承或多态性中扮演什么角色是不明确的。能不能请一些人用一个工作示例来解释,说明它到底是做什么的

  • 问题内容: 使用Jackson 2,我正在寻找一种 通用的 方式将对象序列化为单个值(然后序列化它们,然后再填充该单个字段),而不必重复创建JsonSerializer / JsonDeserializer来处理每种情况。@JsonIdentityInfo批注非常接近,但由于我知道,它将始终对完整的子对象进行序列化,因此略微遗漏了该标记。 这是我想做的一个例子。给定的类: 我希望Order可以序列