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