简体   繁体   English

使用Jackson进行不可变/多态POJO < - > JSON序列化

[英]Immutable/polymorphic POJO <-> JSON serialization with Jackson

I'm trying to serialize a immutable POJO to and from JSON, using Jackson 2.1.4, without having to write a custom serializer and with as few annotations as possible. 我正在尝试使用Jackson 2.1.4将不可变的POJO与JSON序列化,而不必编写自定义序列化程序并尽可能少注释。 I also like to avoid having to add unnecessary getters or default constructors just to satisfy the Jackson library. 我还想避免为了满足Jackson库而添加不必要的getter或默认构造函数。

I'm now stuck on the exception: 我现在卡在异常上:

JsonMappingException: No suitable constructor found for type [simple type, class Circle]: can not instantiate from JSON object (need to add/enable type information?) JsonMappingException:找不到类型[simple type,class Circle]的合适构造函数:无法从JSON对象实例化(需要添加/启用类型信息?)

The code: 编码:

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

The test code: 测试代码:

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

Any help is greatly appreciated, thanks! 非常感谢任何帮助,谢谢!

You could (according to the API) annotate the constructor with @JsonCreator and the parameters with @JsonProperty . 你可以(根据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;
    }
}

Edit: Maybe you have to annotate the Shape class with @JsonSubTypes so that the concrete subclass of Shape could be determined. 编辑:也许您必须使用@JsonSubTypes注释Shape类,以便可以确定Shape的具体子类。

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

Have a look at Genson library some of its key features are adressing your exact problem: polymorphism, not requiring annotations and most important immutable pojos. 看看Genson库,它的一些关键特性正在解决您的确切问题:多态性,不需要注释和最重要的不可变pojos。 Everything works in your example with 0 annotations or heavy conf. 一切都在你的例子中有0注释或沉重的conf。

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 gives you also the ability to use aliases (used instead classes names). Genson还为您提供了使用别名的能力(使用别名类)。

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

Rectangle has two parameters, and the FAQ says: Rectangle有两个参数, FAQ说:

Deserializing simple types 反序列化简单类型

If I want to deserialize simple JSON values (Strings, integer / decimal numbers) into types other than supported by default, do I need to write a custom deserializer? 如果我想将简单的JSON值(字符串,整数/十进制数)反序列化为默认支持的类型以外的类型,我是否需要编写自定义反序列化器?

Not necessarily. 不必要。 If the class to deserialize into has one of: 如果要反序列化的类具有以下之一:

  • Single-argument constructor with matching type (String, int/double), or 具有匹配类型(String,int / double)或的单参数构造函数
  • Single-argument static method with name "valueOf()", and matching argument type 名为“valueOf()”的单参数静态方法,以及匹配的参数类型

Jackson will use such method, passing in matching JSON value as argument. Jackson将使用这种方法,传递匹配的JSON值作为参数。

I'm afraid you have to write your own deserializer as show in the Jackson documentation : 我担心你必须在Jackson文档中编写自己的反序列化程序

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM