Jackson序列化:将字段值设置为XML元素名称

我们使用Jackson jax-rs XML内容提供程序在我们基于jax-rs的REST API项目中处理XML内容类型。 在序列化POJO列表时,我们需要从POJO中的字段动态设置xml元素名称。

public class ResponsePOJO { @JacksonXmlProperty @JacksonXmlElementWrapper(useWrapping = false) private List message = new ArrayList(); } public class Message { private String type; // "Error" or "Warning" private String msg; // The actual message } 

默认Jackson序列化XML:

   Error Some random error message   Warning Some random warning message   

我们的要求,即将类型设置为XML元素名称。

   Some random error message   Some random warning message   

为了实现这一点,我们以下列方式编写了一个自定义XML序列化程序:

 public class MessageListSerializer extends JsonSerializer<List> { @Override public void serialize(List value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { for(Message me : value){ jgen.writeObjectField(me.getType(), me); } } } 

并使用注释添加了序列化程序:

 @JacksonXmlProperty @JacksonXmlElementWrapper(useWrapping = false) @JsonSerialize(using=MessageListSerializer.class) private List message = new ArrayList(); 

但是在使用Jackson XMLMapper序列化ResponsePOJO时,我们得到以下exception……

 Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Array index out of range: -2 at com.fasterxml.jackson.dataformat.xml.ser.XmlSerializerProvider.serializeValue(XmlSerializerProvider.java:100) at com.fasterxml.jackson.databind.ObjectMapper._configAndWriteValue(ObjectMapper.java:2866) at com.fasterxml.jackson.databind.ObjectMapper.writeValue(ObjectMapper.java:2289) Caused by: java.lang.ArrayIndexOutOfBoundsException: Array index out of range: -2 at com.ctc.wstx.sw.BufferingXmlWriter.writeRaw(BufferingXmlWriter.java:241) at com.ctc.wstx.sw.BaseStreamWriter.writeRaw(BaseStreamWriter.java:1113) at com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator.writeRaw(ToXmlGenerator.java:592) at com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter$Lf2SpacesIndenter.writeIndentation(DefaultXmlPrettyPrinter.java:517) at com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter.writeEndObject(DefaultXmlPrettyPrinter.java:223) at com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator.writeEndObject(ToXmlGenerator.java:422) at com.fasterxml.jackson.dataformat.xml.ser.XmlBeanSerializer.serialize(XmlBeanSerializer.java:119) at com.fasterxml.jackson.dataformat.xml.ser.XmlSerializerProvider.serializeValue(XmlSerializerProvider.java:92) ... 3 more 

你能帮我解决这个问题吗?

编辑到以前的解决方案:你几乎就在那里,只需要将@JsonIgnore添加到private String type; // "Error" or "Warning" private String type; // "Error" or "Warning"

   error message   warning message   

以下将输出上面的xml:

 import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.dataformat.xml.XmlMapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { Main demo = new Main(); demo.run(); } public void run(){ ObjectMapper xmlMapper = new XmlMapper(); ResponsePOJO responsePOJO = new ResponsePOJO(); Message message = new Message(); message.setType("Error"); message.setMsg("error message"); Message message2 = new Message(); message2.setType("Warning"); message2.setMsg("warning message"); responsePOJO.getMessage().add(message); responsePOJO.getMessage().add(message2); try { String xml = xmlMapper.writeValueAsString(responsePOJO); System.out.println(xml); } catch (JsonProcessingException e) { e.printStackTrace(); } } public class ResponsePOJO { @JacksonXmlProperty @JacksonXmlElementWrapper(useWrapping = false) @JsonSerialize(using=MessageListSerializer.class) private List message = new ArrayList(); public List getMessage() { return message; } } public class Message { @JsonIgnore private String type; // "Error" or "Warning" private String msg; // The actual message public String getType() { return type; } public void setType(String type) { this.type = type; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } } } 

和class级一起

 import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import java.io.IOException; import java.util.List; /** * Created by Pand on 08/04/2015. */ public class MessageListSerializer extends JsonSerializer> { @Override public void serialize(List value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException { for(Main.Message me : value){ jgen.writeObjectField(me.getType(), me); } } } 

与依赖

    com.fasterxml.jackson.dataformat jackson-dataformat-xml 2.4.0   org.codehaus.woodstox woodstox-core-asl 4.1.4   

我的观点是你的方法过于复杂。 我会改为将你的消息类重组为:

public class Message { private String msg; // The actual message } public class Message { private String msg; // The actual message } ,并根据类型对其进行子类化:

public class Error extends Message { }

public class Warning extends Message { }

此外,此方法允许您为每种类型添加更灵活的自定义字段。

“我不能发表评论,因为它太长了”以下是自定义类:

  @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class MyResponse { @XmlElements({ @XmlElement(name = "error", type = MyError.class), @XmlElement(name = "warning", type = MyWarning.class) }) @XmlElementWrapper private List messages = Lists.newArrayList(); public List getMessages() { return messages; } public void setMessages(List messages) { this.messages = messages; } } @XmlAccessorType(XmlAccessType.FIELD) public class MyMessage { protected String text; public String getText() { return text; } public void setText(String text) { this.text = text; } } @XmlAccessorType(XmlAccessType.FIELD) public class MyError extends MyMessage { } @XmlAccessorType(XmlAccessType.FIELD) public class MyWarning extends MyMessage { } 

我用我的演示代码测试了它:

 MyResponse myResponse = new MyResponse(); MyMessage error = new MyError(); error.setText("error"); MyMessage warning = new MyWarning(); warning.setText("warning"); myResponse.setMessages(Lists.newArrayList(error, warning)); 

它返回了:

     error   warning    

您需要调整元素名称以获得所需的结果。

类似的问题给我。 我编写了一个自定义JsonSerializer来为集合中的每个项生成不同的xml元素名称(从@JsonTypeName读取)。

这是我的JsonSerializer:

 import com.fasterxml.jackson.annotation.JsonTypeName; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; public class NamedCollectionXmlSerializer extends JsonSerializer> { @Override public void serialize(Collection list, JsonGenerator gen, SerializerProvider provider) throws IOException { boolean toXml = gen instanceof ToXmlGenerator; if (!toXml) { // fallback to the default behavior for non-xml serialization gen.writeObject(list); return; } gen.writeStartArray(); if (list != null) { for (Object item : list) { if (item == null) { continue; } JsonTypeName jsonTypeName; if ((jsonTypeName = item.getClass().getAnnotation(JsonTypeName.class)) != null) { // read JsonTypeName as the xml element name // if JsonTypeName not present, use the default name ((ToXmlGenerator) gen).setNextName(new QName("", jsonTypeName.value())); } gen.writeObject(item); } } gen.writeEndArray(); } } 

对于以下POJO(由lombok生成的getter和构造函数):

 interface Message { } @Getter @RequiredArgsConstructor class ResponsePOJO { @JacksonXmlElementWrapper(useWrapping = false) @JsonSerialize(using = NamedCollectionXmlSerializer.class) private final List messages; } @Getter @RequiredArgsConstructor @JsonTypeName("Error") class Error implements Message { private final String msg; } @Getter @RequiredArgsConstructor @JsonTypeName("Warning") class Warning implements Message { private final String msg; } 

和测试代码:

 ResponsePOJO response = new ResponsePOJO( Arrays.asList(new Error("error1"), new Warning("warn1"), new Error("error2")) ); new XmlMapper().writerWithDefaultPrettyPrinter().writeValue(System.out, response); 

这是输出:

   error1   warn1   error2   

PS:我用jackson版本2.9.3测试我的代码