【问题标题】:How to implements polymorphic serialization with out annotation and mixin如何在没有注释和mixin的情况下实现多态序列化
【发布时间】:2022-01-11 10:21:33
【问题描述】:

在jackson中,我们可以使用注解

  1. @JsonTypeInfo
  2. @JsonSubTypes
  3. @JsonSubTypes.Type

实现多态序列化。

我们可以选择

  1. 直接在数据模型上使用这些注解,这是最简单的方法。
  2. 在 mixin 上使用这些注释。这是一个关于它的链接Polymorphic deserialization in Jackson without annotations

这两种方案都有一个问题:编写代码时必须知道所有的子类。

在 GraphQL 中

  1. 鉴别器字段是固定的:“__typename”
  2. 子类型名称也是固定的:Java 类的简单名称

所有需求都是固定的,这意味着不需要一个个配置子类型,可以创建一个杰克逊模块来自动处理它们。

// An empty interface
// Developers need not to configure polymorphic metadata for any class of its subtypes 
public interface GraphQLObject {}

public class BookStore implements GraphQLObject {
    public List<Book> getBooks() {...}
    ...other gettes/setters...
}
public abstract class Book implements GraphQLObject {
    ... some properties ...
}
public class ElectronicBook extends Book {
    ... some properties ...
}
public class PaperBook extends Book {
    ... some properties ...
}

使用代码如下所示

BookStore store = ...;
ObjectMapper mapper = new ObjectMapper();
mapper.addModule(new GraphQLModule());
System.out.println(mapper.writeValueAsString(store));

这里,我们需要创建“GraphQLModule”,它可以处理所有子类型实现空接口“GraphQLObject”,并告诉jackson如何使用每个子类型的简单类名作为判别器字段“__typename”的值"

结果应该是这样的:

{
   name: "store",
   books: [
     { __typename: "ElectronicBook", name: "book-1" },
     { __typename: "PaperBook", name: "book-2" }
   ]
}

是否可以实现“GraphQLModule”?

注意:

与jackson默认的多态行为一样,只有当对象运行时类型与编译时已知的list的泛型类型参数不同时才需要添加discriminator字段。

【问题讨论】:

    标签: java json jackson


    【解决方案1】:

    我找到了原因。

    我尝试定义客户序列化程序,但我发现从未调用过“serializeWithType”。

    在我的项目中,数据类型是接口。我使用 ASM 来生成它的字节码。我只生成了最简单的字节码,忽略了泛型的签名。

    所以,在界面中,它是 List

    但是,在我的字节码实现中,它是 List

    【讨论】:

      【解决方案2】:

      可以实现扩展SimpleModule类的“GraphQLModule”模块:

      public class GraphQLModule extends SimpleModule {
      
          public GraphQLModule() {
              this.addSerializer(new GraphQLSerializer());
          }
      }
      

      我在模块中添加了一个新的序列化程序,它扩展了StdSerializer 类:

      public class GraphQLSerializer extends StdSerializer<GraphQLObject> {
      
          public GraphQLSerializer() {
              super(GraphQLObject.class);
          }
      
          @Override
          public void serialize(GraphQLObject obj, JsonGenerator jg, SerializerProvider sp) throws IOException {
              jg.writeStartObject();
              jg.writeStringField("__typename", obj.getClass().getSimpleName());
              jg.writeEndObject();
          }
          
      }
      

      GraphQLSerializer 序列化程序只需将您的对象实现您的GraphQLObject 接口并将其序列化,包括在 json 中只是对象的类名字符串作为__typename

      因此您可以将此模块添加到您的objectMapper 并像在此示例中一样使用它:

      public interface GraphQLObject {}
      public abstract class Book implements GraphQLObject {}
      public class ElectronicBook extends Book {}
      public class PaperBook extends Book {}
      
      ObjectMapper objectMapper = new ObjectMapper();
      mapper.registerModule(new GraphQLModule());
      List<Book> books = List.of(new ElectronicBook(), new PaperBook());
      //it will print [{"__typename":"ElectronicBook"},{"__typename":"PaperBook"}]
      System.out.println(mapper.writeValueAsString(books));
      

      【讨论】:

      • 感谢您这么长时间的回复。对不起,我忘记了一点。这是我目前的解决方案。目标:像杰克逊的默认多态行为一样,只有在必要时才添加该字段。 ``` var store1 = new BookStore(); store1.setBooks(List.of(new Book(), new Book())); var store2 = new BookStore(); store2.setBooks(List.of(new ElectronicBook(), new Book())); ``` store1,所有对象都匹配 List 的通用参数,不需要。 store2,与该通用参数不匹配,因此很有必要。再次为没有清楚地描述问题而道歉。
      猜你喜欢
      • 2011-06-15
      • 1970-01-01
      • 2019-08-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-11
      • 1970-01-01
      相关资源
      最近更新 更多