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

时间:2015-04-08 13:27:07

标签: java xml jackson jax-rs

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

public class ResponsePOJO {
     @JacksonXmlProperty
     @JacksonXmlElementWrapper(useWrapping = false)
     private List<Message> message = new ArrayList<Message>();
}

public class Message {
     private String type; // "Error" or "Warning"
     private String msg; // The actual message
}

默认杰克逊序列化XML:

<ResponsePOJO>
    <message>
        <type>Error</type>
        <msg>Some random error message</msg>
    </message>
    <message>
        <type>Warning</type>
        <msg>Some random warning message</msg>
    </message>
</ResponsePOJO>

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

<ResponsePOJO>
    <Error>
        <msg>Some random error message</msg>
    </Error>
    <Warning>
        <msg>Some random warning message</msg>
    </Warning>
</ResponsePOJO>

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

public class MessageListSerializer extends
        JsonSerializer<List<Message>> {
    @Override
    public void serialize(List<Message> 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> message = new ArrayList<Message>();

但是在使用Jackson XMLMapper序列化ResponsePOJO时,我们得到以下异常......

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

你能帮我解决这个问题......

4 个答案:

答案 0 :(得分:1)

编辑上一个解决方案: 您就在那里,只需要将@JsonIgnore添加到private String type; // "Error" or "Warning"

<ResponsePOJO>
    <Error>
        <msg>error message</msg>
    </Error>
    <Warning>
        <msg>warning message</msg>
    </Warning>
</ResponsePOJO>

以下将输出上面的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> message = new ArrayList<Message>();

        public List<Message> 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;
        }
    }


}

与班级一起

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<List<Main.Message>> {


    @Override
    public void serialize(List<Main.Message> value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {

        for(Main.Message me : value){
            jgen.writeObjectField(me.getType(), me);
        }
    }


}

带有依赖关系

    <dependencies>
        <dependency>
            <groupId>com.fasterxml.jackson.dataformat</groupId>
            <artifactId>jackson-dataformat-xml</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.codehaus.woodstox</groupId>
            <artifactId>woodstox-core-asl</artifactId>
            <version>4.1.4</version>
        </dependency>
    </dependencies>

答案 1 :(得分:0)

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

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

public class Error extends Message { }

public class Warning extends Message { }

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

答案 2 :(得分:0)

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

 @XmlRootElement
 @XmlAccessorType(XmlAccessType.FIELD)
 public class MyResponse {

 @XmlElements({ @XmlElement(name = "error", type = MyError.class),
  @XmlElement(name = "warning", type = MyWarning.class) })
 @XmlElementWrapper
  private List<MyMessage> messages = Lists.newArrayList();

  public List<MyMessage> getMessages() {
  return messages;
 }

 public void setMessages(List<MyMessage> 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));

然后又回来了:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myResponse> 
  <messages>
    <error>
      <text>error</text>
    </error>
    <warning>
      <text>warning</text>
    </warning>
  </messages>
</myResponse> 

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

答案 3 :(得分:0)

对我来说类似的问题。我编写了一个自定义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<Collection<Object>> {
    @Override
    public void serialize(Collection<Object> 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<Message> 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);

这是输出:

<ResponsePOJO>
  <Error>
    <msg>error1</msg>
  </Error>
  <Warning>
    <msg>warn1</msg>
  </Warning>
  <Error>
    <msg>error2</msg>
  </Error>
</ResponsePOJO>

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