12

我正在尝试使用 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);

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

4

3 回答 3

10

您可以(根据 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 {}
于 2013-02-27T20:38:13.117 回答
3

看看Genson库,它的一些关键特性正在解决您的确切问题:多态性、不需要注释和最重要的不可变 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();
于 2013-02-27T21:42:47.460 回答
1

Rectangle 有两个参数,FAQ说:

反序列化简单类型

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

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

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

Jackson 将使用这种方法,将匹配的 JSON 值作为参数传入。

恐怕您必须编写自己的反序列化器,如杰克逊文档中所示

ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule =
   new SimpleModule("MyModule", new Version(1, 0, 0, null))
      .addDeserializer( MyType.class, new MyTypeDeserializer());
mapper.registerModule( testModule );
于 2013-02-27T20:29:52.040 回答