Multiple Jackson XML Custom (XMLStreamWriter) Serialisers throws Exception

我的未来我决定 提交于 2020-01-15 07:21:27

问题


If there are multiple custom XML Serializers (XMLStreamWriter) in a class, serialization fails.

I have two classes: CustomClass1, CustomClass2. There is a wrapping class TestJacksonXml1. When I am trying to serialize TestJacksonXml1, it throws an exception.

CustomClass1

class CustomClass1 {
    int prop1;

    public CustomClass1(int prop1) {
        this.prop1 = prop1;
    }

    public int getProp1() {
        return prop1;
    }

    static class CustomClass1Serializer extends StdSerializer<CustomClass1> {
        public CustomClass1Serializer() { this(null); }

        public CustomClass1Serializer(Class<CustomClass1> t) {
            super(t);
        }

        @Override
        public void serialize(CustomClass1 customClass1, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            final ToXmlGenerator toXmlGenerator = (ToXmlGenerator) jsonGenerator;
            final XMLStreamWriter staxWriter = (toXmlGenerator).getStaxWriter();
            try {
                staxWriter.writeStartElement("class1");
                staxWriter.writeCharacters(String.valueOf(customClass1.prop1));
                staxWriter.writeEndElement();
            } catch (XMLStreamException e){
                e.printStackTrace();
            }
        }
    }
}

CustomClass2

class CustomClass2 {
        int prop2;

        public CustomClass2(int prop2) {
            this.prop2 = prop2;
        }

        public int getProp2() {
            return prop2;
        }

        static class CustomClass2Serializer extends StdSerializer<CustomClass2> {
            public CustomClass2Serializer() { this(null); }

            public CustomClass2Serializer(Class<CustomClass2> t) {
                super(t);
            }

            @Override
            public void serialize(CustomClass2 customClass2, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                final ToXmlGenerator toXmlGenerator = (ToXmlGenerator) jsonGenerator;
                final XMLStreamWriter staxWriter = (toXmlGenerator).getStaxWriter();
                try {
                    staxWriter.writeStartElement("class2");
                    staxWriter.writeCharacters(String.valueOf(customClass2.prop2));
                    staxWriter.writeEndElement();
                } catch (XMLStreamException e){
                    e.printStackTrace();
                }
            }
        }
    }

Enclosing Class

public class TestJacksonXml1 {
    @JsonSerialize(using = CustomClass1.CustomClass1Serializer.class)
    CustomClass1 obj1;

    @JsonSerialize(using = CustomClass2.CustomClass2Serializer.class)
    CustomClass2 obj2;

    public TestJacksonXml1(CustomClass1 obj1, CustomClass2 obj2) {
        this.obj1 = obj1;
        this.obj2 = obj2;
    }

    public CustomClass1 getObj1() {
        return obj1;
    }

    public CustomClass2 getObj2() {
        return obj2;
    }

    public static void main(String[] args) throws JsonProcessingException {
        XmlMapper xmlMapper = new XmlMapper();
        System.out.println(xmlMapper.writeValueAsString(new TestJacksonXml1(new CustomClass1(10), new CustomClass2(20))));
    }
}

The exception I get is

Exception in thread "main" com.fasterxml.jackson.core.JsonGenerationException: Can not write a field name, expecting a value
    at com.fasterxml.jackson.core.JsonGenerator._reportError(JsonGenerator.java:1961)
    at com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator.writeFieldName(ToXmlGenerator.java:435)
    at com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator.writeFieldName(ToXmlGenerator.java:577)
    at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:725)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializerBase.serializeFields(XmlBeanSerializerBase.java:202)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer.serialize(XmlBeanSerializer.java:117)
    at com.fasterxml.jackson.dataformat.xml.ser.XmlSerializerProvider.serializeValue(XmlSerializerProvider.java:107)
    at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:3905)
    at com.fasterxml.jackson.databind.ObjectMapper.writeValueAsString(ObjectMapper.java:3219)
    at fk.reportsvc.common.TestJacksonXml1.main(TestJacksonXml1.java:39)

When I comment out one of the custom serializers, the other one works. Why is it so?

Shouldn't we use two/multiple custom serializers (with XMLStreamWriter) at once?

If I use JsonGenerator directly instead of XMLStreamWriter, then I am able to use both custom serializers at once.

PS: My actual business classes contain many fields that are to be converted into nested XML elements and attributes. Hence preferring XMLStreamWriter directly over others.


回答1:


Much simpler would be to use methods from ToXmlGenerator class. Take a look at: setNextName, writeRaw and writeRepeatedFieldName methods. In your case, implementation could look like below:

class CustomClass2Serializer extends StdSerializer<CustomClass2> {

    private final QName name = new QName("class2");

    public CustomClass2Serializer() {
        super(CustomClass2.class);
    }

    @Override
    public void serialize(CustomClass2 customClass2, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        final ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
        xmlGenerator.setNextName(name);
        xmlGenerator.writeStartObject();
        xmlGenerator.writeRaw(String.valueOf(customClass2.prop2));
        xmlGenerator.writeRepeatedFieldName();
        xmlGenerator.writeEndObject();
    }
}

and:

class CustomClass1Serializer extends StdSerializer<CustomClass1> {

    private final QName name = new QName("class1");

    public CustomClass1Serializer() {
        super(CustomClass1.class);
    }

    @Override
    public void serialize(CustomClass1 customClass1, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        final ToXmlGenerator xmlGenerator = (ToXmlGenerator) jsonGenerator;
        xmlGenerator.setNextName(name);
        xmlGenerator.writeStartObject();
        xmlGenerator.writeRaw(String.valueOf(customClass1.prop1));
        xmlGenerator.writeRepeatedFieldName();
        xmlGenerator.writeEndObject();
    }
}

Generated XML should look like below:

<TestJacksonXml1>
  <class1>10</class1>
  <class2>20</class2>
</TestJacksonXml1>

Main problem in your case is you want to skip object node for CustomClass1 and CustomClass1 classes. Much simpler would be implementing serialiser for TestJacksonXml1 class:

class TestJacksonXml1JsonSerializer extends JsonSerializer<TestJacksonXml1> {
    @Override
    public void serialize(TestJacksonXml1 value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        gen.writeStartObject();
        gen.writeStringField("class1", String.valueOf(value.obj1.prop1));
        gen.writeStringField("class2", String.valueOf(value.obj2.prop2));
        gen.writeEndObject();
    }
}

Now, TestJacksonXml1 class should look like below:

@JsonSerialize(using = TestJacksonXml1JsonSerializer.class)
class TestJacksonXml1 {

    CustomClass1 obj1;
    CustomClass2 obj2;
    // getters, setters, etc
}

It should generate the same output but is much easier to implement.



来源:https://stackoverflow.com/questions/57275271/multiple-jackson-xml-custom-xmlstreamwriter-serialisers-throws-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!