Джексон добавить обертку на основе значений поля - PullRequest
1 голос
/ 07 октября 2019

У меня есть класс, как показано ниже:

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

К сожалению, мне нужно сериализовать его примерно так:

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

Я могу получить его до такой степени, что у меня естьсписок ASSET объектов внутри ASSETS, но как мне добавить эту дополнительную оболочку, которая состоит из двух полей?

1 Ответ

1 голос
/ 07 октября 2019

Вам необходимо написать собственный сериализатор. Для этого необходимо расширить класс com.fasterxml.jackson.databind.JsonSerializer. Также для создания дополнительного элемента переноса используйте метод startWrappedValue . Пример кода может выглядеть следующим образом:

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>

Решение с использованием сериализатора по умолчанию для огромного компонента

Использование сериализатора по умолчанию для компонентаAsset класс, вам нужно использовать BeanSerializerModifier и зарегистрировать его с помощью SimpleModule. Нам нужно вызвать метод serializeFields, который защищен, поэтому я создал ExpandXmlBeanSerializer просто для того, чтобы сделать его общедоступным, чтобы мы могли использовать его в нашей реализации:

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. НесколькоПользовательские XML-файлы Jackson (XMLStreamWriter) генерируют исключение
  4. SpringBoot: использование и создание XML с помощью настраиваемого сериализатора + десериализатора

  5. Ошибка сообщения SNS десериализации Джексона MismatchedInputException

...