【问题标题】:Immutable/polymorphic POJO <-> JSON serialization with Jackson不可变/多态 POJO <-> JSON 序列化与 Jackson
【发布时间】:2013-02-27 20:22:28
【问题描述】:

我正在尝试使用 Jackson 2.1.4 将不可变的 POJO 序列化到 JSON 和从 JSON 序列化,而无需编写自定义序列化程序并使用尽可能少的注释。我还想避免为了满足 Jackson 库而添加不必要的 getter 或默认构造函数。

我现在遇到了异常:

JsonMappingException:没有找到适合类型[简单类型,类 Circle] 的构造函数:无法从 JSON 对象实例化(需要添加/启用类型信息?)

代码:

public abstract class Shape {}


public class Circle extends Shape {
  public final int radius; // Immutable - no getter needed

  public Circle(int radius) {
    this.radius = radius;
  }
}


public class Rectangle extends Shape {
  public final int w; // Immutable - no getter needed
  public final int h; // Immutable - no getter needed

  public Rectangle(int w, int h) {
    this.w = w;
    this.h = h;
  }
}

测试代码:

ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY); // Adds type info

Shape circle = new Circle(10);
Shape rectangle = new Rectangle(20, 30);

String jsonCircle = mapper.writeValueAsString(circle);
String jsonRectangle = mapper.writeValueAsString(rectangle);

System.out.println(jsonCircle); // {"@class":"Circle","radius":123}
System.out.println(jsonRectangle); // {"@class":"Rectangle","w":20,"h":30}

// Throws:
//  JsonMappingException: No suitable constructor found.
//  Can not instantiate from JSON object (need to add/enable type information?)
Shape newCircle = mapper.readValue(jsonCircle, Shape.class);
Shape newRectangle = mapper.readValue(jsonRectangle, Shape.class);

System.out.println("newCircle = " + newCircle);
System.out.println("newRectangle = " + newRectangle);

非常感谢任何帮助,谢谢!

【问题讨论】:

    标签: java json inheritance jackson immutability


    【解决方案1】:

    您可以(根据 API)使用 @JsonCreator 注释构造函数,使用 @JsonProperty 注释参数。

    public class Circle extends Shape {
        public final int radius; // Immutable - no getter needed
    
        @JsonCreator
        public Circle(@JsonProperty("radius") int radius) {
            this.radius = radius;
        }
    }
    
    public class Rectangle extends Shape {
        public final int w; // Immutable - no getter needed
        public final int h; // Immutable - no getter needed
    
        @JsonCreator        
        public Rectangle(@JsonProperty("w") int w, @JsonProperty("h") int h) {
            this.w = w;
            this.h = h;
        }
    }
    

    编辑:也许您必须使用 @JsonSubTypes 注释 Shape 类,以便确定 Shape 的具体子类。

    @JsonSubTypes({@JsonSubTypes.Type(Circle.class), @JsonSubTypes.Type(Rectangle.class)})
    public abstract class Shape {}
    

    【讨论】:

    • 看起来很有希望,但现在我得到了以下异常:JsonMappingException: Argument #0 of constructor [constructor for Circle, annotations: {interface com.fasterxml.jackson.annotation.JsonCreator=@com.fasterxml .jackson.annotation.JsonCreator()}] 没有属性名注释;多参数构造函数注解为 Creator 时必须有名字
    • 虽然@JsonProperty 注解存在?
    • 是的,就像你的例子一样。在所有构造函数参数上。
    • 是的 - 你是对的!这些类是嵌套的(在我的测试用例中)并且需要“静态”才能工作!所以,现在一切正常!有趣的是,它不需要 JsonCreator 或 JsonSubTypes 来工作。唯一需要的注释是 JsonProperty!谢谢!
    • 当你认为非静态内部类有“隐藏”的第一个参数将“this”指针传递给父类时,这是有道理的。这是编译器引入的语法糖,它允许非静态内部类引用封闭父类实例的实例字段。
    【解决方案2】:

    看看Genson library 它的一些关键特性正在解决你的确切问题:多态性、不需要注释和最重要的不可变 pojo。在您的示例中一切正常,只需 0 个注释或大量配置。

    Genson genson = new Genson.Builder().setWithClassMetadata(true)
                                .setWithDebugInfoPropertyNameResolver(true)
                                .create();
    
    String jsonCircle = genson.serialize(circle);
    String jsonRectangle = genson.serialize(rectangle);
    
    System.out.println(jsonCircle); // {"@class":"your.package.Circle","radius":123}
    System.out.println(jsonRectangle); // {"@class":"your.package.Rectangle","w":20,"h":30}
    
    // Throws nothing :)
    Shape newCircle = genson.deserialize(jsonCircle, Shape.class);
    Shape newRectangle = genson.deserialize(jsonRectangle, Shape.class);
    

    Genson 还让您能够使用别名(使用类名代替)。

    new Genson.Builder().addAlias("shape", Shape.class)
                    .addAlias("circle", Circle.class)
                    .create();
    

    【讨论】:

      【解决方案3】:

      Rectangle 有两个参数,FAQ 表示:

      反序列化简单类型

      如果我想反序列化简单的 JSON 值(字符串、整数 / 十进制数)转换为默认支持以外的类型,我需要吗 编写自定义反序列化器?

      不一定。如果要反序列化的类具有以下之一:

      • 具有匹配类型(String、int/double)的单参数构造函数,或
      • 名称为“valueOf()”且参数类型匹配的单参数静态方法

      Jackson 将使用这样的方法,将匹配的 JSON 值传递为 论据。

      恐怕你得自己写deserializer as show in the Jackson documentation

      ObjectMapper mapper = new ObjectMapper();
      SimpleModule testModule =
         new SimpleModule("MyModule", new Version(1, 0, 0, null))
            .addDeserializer( MyType.class, new MyTypeDeserializer());
      mapper.registerModule( testModule );
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-07
        • 2020-12-10
        • 2016-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多