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

Java 如何为父类和子类使用单独的自定义json序列化程序?,java,json,serialization,fasterxml,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 se

在下面的示例中,我有一个主类-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();
        }
    }
}
实际产量:

{
    "base" : {
         "CustomKeyA":"A"
    }
}
扩展输出:

{
    "base" : {
         "CustomKeyA":"A"
    },
    "secondaryKey":"B"
}
那个么,如何以自定义方式序列化一个超类成员,并以标准方式序列化所有其他子类成员呢?谢谢


UPD:已更正。

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

@JsonProperty("CustomKeyA")
public final String primaryKey;
Jackson将对类
B
处理
primaryKey
secondaryKey
属性,并分别作为
CustomKeyA
secondaryKey
包含在输出中

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

更新

如果不允许修改原始类
A
B
,仍然可以解决使用序列化程序的问题

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

我可以建议的解决方案是:使用Jackson的机制检索对象的属性作为
映射
(以便所有可输出的属性都在那里),并用适当的键替换父类中的一个。然后将映射输出为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);
    }
  }

}
公共类ASerializer扩展了StdSerializer{
//我们将使用此映射器将对象转换为贴图
私有静态最终ObjectMapper MAPPER=新ObjectMapper();
//包含仅属于父类的属性名称列表
private static final Set BASE_PROPERTIES=new HashSet(Arrays.asList(“primaryKey”);
公共ASerializer(){
这(A级);
}
公用空调器(t类){
超级(t);
}
私有void serializeBaseProperties(值JsonGenerator jgen)引发IOException{
//创建要在“base”下序列化的基本属性及其值的映射
final Map baseProperties=new HashMap();
baseProperties.put(“CustomKeyA”,value.primaryKey);
//输出地图
jgen.writeFieldName(“基”);
jgen.writeObject(基本属性);
}
@抑制警告(“未选中”)
私有属性(值,JsonGenerator jgen){
((Map)MAPPER.convertValue(value,Map.class))//获取所有可序列化的属性
.entrySet().stream()
.filter(entry->!BASE_PROPERTIES.contains(entry.getKey())//从基类中筛选出
.forEach(property->writeProperty(property,jgen));//将自己的属性输出到JSON
}
@凌驾
public void serialize(值、JsonGenerator jgen、SerializerProvider提供程序)引发IOException{
jgen.writeStartObject();
序列化基本属性(值,jgen);
属性(值,jgen);
jgen.writeEndObject();
}
私有void writeProperty(Map.Entry属性,JsonGenerator jgen){
试一试{
writeFieldName(property.getKey());
writeObject(property.getValue());
}捕获(IOEX异常){
抛出新的非法状态异常(ex);
}
}
}
可能还有另一种方法,通过创建与目标类层次结构并行的序列化器层次结构。这也会起作用,但再一次,这会造成冗余,并使其更难维护


更新结束

谢谢,可以,但我无法编辑类,它们是生成的。我有很多从基类继承的类,我需要以自定义方式序列化基类成员。好的,@morbili,我刚刚用另一种方法更新了我的答案,考虑到
a
B
不能更改。谢谢,@Antot,这是一种很好的方法,如果我只需要重命名属性名,但我需要对基类成员进行自定义处理,对其他类成员进行标准处理,这种方法就可以工作。我已经更新了序列化程序,实际的和预期的,现在它应该看起来更精确。嗨,@Antot,你能告诉我更多关于创建序列化程序层次结构的细节吗,或者提供一些链接,谢谢。好吧,经过一些思考后,我得出结论,使用序列化程序层次结构太过黑客化。对于同一个对象,将部分自定义序列化与标准机制相结合,并保持所有其他实现程序的通用性而不会变得过于冗长,这一点并不明显:)不过,我已经再次更新了答案中的代码,以使其更具可读性。我希望这有帮助。
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);
    }
  }

}