簡體   English   中英

Jackson序列化:將字段值設置為XML元素名稱

[英]Jackson Serialization: Setting field value as XML element name

我們使用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
}

默認Jackson序列化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

你能幫我解決這個問題嗎?

編輯到以前的解決方案:你幾乎就在那里,只需要將@JsonIgnore添加到private String type; // "Error" or "Warning" 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>

我的觀點是你的方法過於復雜。 我會改為將你的消息類重組為:

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

您需要調整元素名稱以獲得所需的結果。

我也有類似的問題。 我編寫了一個自定義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:我用傑克遜版本2.9.3測試我的代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM