Jackson 根据字段值添加包装器 [英] Jackson add wrapper based on field values

查看:42
本文介绍了Jackson 根据字段值添加包装器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个如下所示的课程:

I have a class like below:

@JsonRootName("ASSETS")
public class Assets{
    String val1;
    String val2;
}

不幸的是,我需要将它序列化为这样的:

Unfortunately I need to serialize it to something like this:

<ASSETS>
   <ASSET>
      <val1_val2>
         <val1>x</val1>
         <val2>y</val2>
      </val1_val2>
   </ASSET>
   <ASSET>
      <val1_val2>
         <val1>x</val1>
         <val2>y</val2>
      </val1_val2>
   </ASSET>
</ASSETS>

我可以在 ASSETS 中有一个 ASSET 对象列表,但是我如何添加额外的包装器,它是两个的组合字段?

I can get it to the point where I have a list of ASSET objects inside ASSETS, but how do I add that extra wrapper that's a composite of two of the fields?

推荐答案

您需要编写自定义序列化程序.为此,请扩展 com.fasterxml.jackson.databind.JsonSerializer 类.此外,要创建额外的 wrap 元素,请使用 startWrappedValue 方法.示例代码可能如下所示:

You need to write custom serialiser. To do that, extend com.fasterxml.jackson.databind.JsonSerializer class. Also, to create extra wrap element use startWrappedValue method. Example code could look like below:

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;

import javax.xml.namespace.QName;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
        xmlMapper.setDefaultUseWrapper(false);

        Assets assets = new Assets();
        assets.getAssets().add(new Asset());
        assets.getAssets().add(new Asset());

        System.out.println(xmlMapper.writeValueAsString(assets));
    }
}

class AssetXMLSerializer extends JsonSerializer<Asset> {

    private final QName wrapper = new QName("val1_val2");
    @Override
    public void serialize(Asset value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        ToXmlGenerator xmlGen = (ToXmlGenerator) gen;

        xmlGen.writeStartObject();
        xmlGen.startWrappedValue(wrapper, wrapper);
        xmlGen.writeStringField("val1", value.getVal1());
        xmlGen.writeStringField("val2", value.getVal2());
        xmlGen.finishWrappedValue(wrapper, wrapper);
        xmlGen.writeEndObject();

    }
}

@JsonRootName("ASSETS")
class Assets {

    @JacksonXmlProperty(localName = "ASSET")
    private List<Asset> assets = new ArrayList<>();

    // getters, setters, toString
}

@JsonSerialize(using = AssetXMLSerializer.class)
class Asset {
    private String val1;
    private String val2;

    // getters, setters, toString
}

以上代码打印:

<ASSETS>
  <ASSET>
    <val1_val2>
      <val1>x</val1>
      <val2>y</val2>
    </val1_val2>
  </ASSET>
  <ASSET>
    <val1_val2>
      <val1>x</val1>
      <val2>y</val2>
    </val1_val2>
  </ASSET>
</ASSETS>

为一个巨大的 bean 使用默认序列化器的解决方案

要为 Asset 类使用默认 bean 序列化器,您需要使用 BeanSerializerModifier 并使用 SimpleModule 注册它.我们需要调用受保护的 serializeFields 方法,所以我创建了 ExpandXmlBeanSerializer 只是为了将其公开以便我们可以在我们的实现中使用它:

Solution with using default serialiser for a huge bean

To use default bean serialiser for an Asset class you need to use BeanSerializerModifier and register it using SimpleModule. We need to invoke serializeFields method which is protected, so I created ExpandXmlBeanSerializer just to make it public so we can use it in our implementation:

import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer;

import javax.xml.namespace.QName;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        SimpleModule assetModule = new SimpleModule();
        assetModule.setSerializerModifier(new LoopBackBeanSerializerModifier());

        XmlMapper xmlMapper = new XmlMapper();
        xmlMapper.registerModule(assetModule);
        xmlMapper.enable(SerializationFeature.INDENT_OUTPUT);
        xmlMapper.setDefaultUseWrapper(false);

        Assets assets = new Assets();
        assets.getAssets().add(new Asset("x0", "y0"));
        assets.getAssets().add(new Asset("x1", "y1"));

        System.out.println(xmlMapper.writeValueAsString(assets));
    }
}

class LoopBackBeanSerializerModifier extends BeanSerializerModifier {
    @Override
    public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer<?> serializer) {
        if (beanDesc.getBeanClass() == Asset.class) {
            return new AssetXMLSerializer(new ExpandXmlBeanSerializer((BeanSerializerBase) serializer));
        }
        return serializer;
    }
}

class ExpandXmlBeanSerializer extends XmlBeanSerializer {

    public ExpandXmlBeanSerializer(BeanSerializerBase src) {
        super(src);
    }

    @Override
    public void serializeFields(Object bean, JsonGenerator gen0, SerializerProvider provider) throws IOException {
        super.serializeFields(bean, gen0, provider);
    }
}

class AssetXMLSerializer extends JsonSerializer<Asset> {

    private final QName wrapper;
    private final ExpandXmlBeanSerializer baseSerializer;

    public AssetXMLSerializer(ExpandXmlBeanSerializer baseSerializer) {
        this.baseSerializer = Objects.requireNonNull(baseSerializer);
        String fields = String.join("_",
                Stream.of(Asset.class.getDeclaredFields())
                .map(Field::getName)
                .collect(Collectors.toList()));
        this.wrapper = new QName(fields);
    }

    @Override
    public void serialize(Asset value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        ToXmlGenerator xmlGen = (ToXmlGenerator) gen;

        xmlGen.writeStartObject();
        xmlGen.startWrappedValue(wrapper, wrapper);
        baseSerializer.serializeFields(value, gen, serializers);
        xmlGen.finishWrappedValue(wrapper, wrapper);
        xmlGen.writeEndObject();

    }
}

@JsonRootName("ASSETS")
class Assets {

    @JacksonXmlProperty(localName = "ASSET")
    private List<Asset> assets = new ArrayList<>();

    // getters, setters, toString
}

class Asset {
    private String val1;
    private String val2;

    public Asset(String val1, String val2) {
        this.val1 = val1;
        this.val2 = val2;
    }

    // getters, setters, toString
}

另见:

  1. Jackson xml 和 json 根元素
  2. 使用 Jackson 将 XML 属性添加到手动构建的节点树
  3. 多个 Jackson XML 自定义 (XMLStreamWriter) 序列化程序抛出异常
  4. SpringBoot:消费&使用自定义序列化器 + 反序列化器生成 XML

Jackson 反序列化 SNS 消息错误 MismatchedInputException

这篇关于Jackson 根据字段值添加包装器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆