如何为父类和子类使用单独的自定义json序列化程序?

时间:2018-04-02 20:08:30

标签: java json serialization fasterxml

在下面的例子中,我有一个主要类--A及其子类 - B:

public class A
{
   public final String primaryKey;

   A(String primaryKey)
   {
        this.primaryKey = primaryKey;
   }
}

public class B extends A
{
   public final String secondaryKey;

   B(String primaryKey, String secondaryKey)
   {
        super(primaryKey);
        this.secondaryKey = secondaryKey;
   }
}

我也有自定义序列化器:

public class ASerializer extends StdSerializer<A> {
    public ASerializer(Class<A> t)
         super(t);
    }

    @Override
    public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) {
        try {
            jgen.writeStartObject();
            jgen.writeFieldName("base");
            jgen.writeStartObject();
            jgen.writeStringField("CustomKeyA", value.primaryKey);
            jgen.writeEndObject();
            jgen.writeEndObject();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

因此,以下代码使用自定义序列化程序进行json序列化:

ObjectMapper mapper = new ObjectMapper();
SimpleModule testModule = new SimpleModule("testModule", Version.unknownVersion());
testModule.addSerializer(A.class, new ASerializer(A.class));
result = mapper.writeValueAsString(new B("A", "B"));
System.out.println(result);

实际输出:

{
    "base" : {
         "CustomKeyA":"A"
    }
}

Expexted输出:

{
    "base" : {
         "CustomKeyA":"A"
    },
    "secondaryKey":"B"
}

那么,如何以标准方式自定义方式和所有其他子类成员序列化超类成员?感谢。

UPD:已更正。

1 个答案:

答案 0 :(得分:1)

如果您的用例仅限于为类A字段定义特定名称,那么您不需要来实现和注册自定义序列化程序。通过添加

可以实现所需的输出
@JsonProperty("CustomKeyA")
public final String primaryKey;

杰克逊会为primaryKey课程处理secondaryKeyB属性,并分别作为CustomKeyAsecondaryKey添加到输出中。

使用自定义序列化程序可以完全控制要输出的元素,绕过默认的序列化机制:这意味着必须手动处理属性&#34;这对于类层次结构很容易变得复杂。

更新

如果您不允许修改原始类AB,则仍然可以使用序列化程序进行解决方法。

该方法的主要困难在于,父类A的序列化程序对从A继承的对象的属性了解不多。检查特定类型,转换和输出相应的字段将非常困难。维持这将特别地难道。

我可以建议的解决方案的想法是:使用Jackson的机制检索对象的属性为Map(以便所有可输出属性都在那里)并替换其中一个具有适当键的父类。然后将地图输出到JSON。

这可以实现如下:

public class ASerializer extends StdSerializer<A> {

  // we'll use this mapper to convert the object into a Map
  private static final ObjectMapper MAPPER = new ObjectMapper();

  // contains the list of property names that belong to parent class only
  private static final Set<String> BASE_PROPERTIES = new HashSet<>(Arrays.asList("primaryKey"));

  public ASerializer() {
    this(A.class);
  }

  public ASerializer(Class<A> t) {
    super(t);
  }

  private void serializeBaseProperties(A value, JsonGenerator jgen) throws IOException {
    // create a Map of base properties and their values to serialize under "base"
    final Map<String, Object> baseProperties = new HashMap<>();
    baseProperties.put("CustomKeyA", value.primaryKey);
    // output the map
    jgen.writeFieldName("base");
    jgen.writeObject(baseProperties);
  }

  @SuppressWarnings("unchecked")
  private void serializeOwnProperties(A value, JsonGenerator jgen) {
    ((Map<String, Object>) MAPPER.convertValue(value, Map.class)) // grab all serializable properties
      .entrySet().stream()
      .filter(entry -> !BASE_PROPERTIES.contains(entry.getKey())) // filter out the ones from base class
      .forEach(property -> writeProperty(property, jgen)); // output own properties to JSON
  }

  @Override
  public void serialize(A value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
    jgen.writeStartObject();
    serializeBaseProperties(value, jgen);
    serializeOwnProperties(value, jgen);
    jgen.writeEndObject();
  }

  private void writeProperty(Map.Entry<String, Object> property, JsonGenerator jgen) {
    try {
      jgen.writeFieldName(property.getKey());
      jgen.writeObject(property.getValue());
    } catch (IOException ex) {
      throw new IllegalStateException(ex);
    }
  }

}

通过创建序列化程序层次结构,可能还有另一种方法,与目标类层次结构并行。这也会起作用,但是再一次,这会造成冗余并使其难以维护。

结束更新