简体   繁体   English

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

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

We are using Jackson jax-rs XML content providers for handling XML content type in our jax-rs based REST API project. 我们使用Jackson jax-rs XML内容提供程序在我们基于jax-rs的REST API项目中处理XML内容类型。 In the serializing a List of POJOs, we need to set the xml element name dynamically from a field in the POJO. 在序列化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
}

Default Jackson serialized XML: 默认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>

Our requirement, ie., set type as the XML element name. 我们的要求,即将类型设置为XML元素名称。

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

In order to achieve this, we wrote a custom XML serializer in the following manner: 为了实现这一点,我们以下列方式编写了一个自定义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);
        }
    }
}

And added the serializer using annotation: 并使用注释添加了序列化程序:

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

But while serializing the ResponsePOJO using Jackson XMLMapper, we are getting the following exception... 但是在使用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

Could you please help me resolve this issue... 你能帮我解决这个问题吗?

Edit to previous solution: You're nearly there, just need to add @JsonIgnore to private String type; // "Error" or "Warning" 编辑到以前的解决方案:你几乎就在那里,只需要将@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>

The following will output the above xml: 以下将输出上面的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;
        }
    }


}

along with the 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<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);
        }
    }


}

with dependencies 与依赖

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

My opinion is that your approach is over complicated. 我的观点是你的方法过于复杂。 I would instead restructure your message class as: 我会改为将你的消息类重组为:

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

public class Error extends Message { }

public class Warning extends Message { } . public class Warning extends Message { }

In addition, this approach allows you adding customized fields to each type which is more flexible. 此外,此方法允许您为每种类型添加更灵活的自定义字段。

"I cannot post it in comment since it is too long" The following is the customized classes: “我不能发表评论,因为它太长了”以下是自定义类:

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

} 

I tested it with my demo code: 我用我的演示代码测试了它:

MyResponse myResponse = new MyResponse();
MyMessage error = new MyError();
error.setText("error");

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

and it returned: 它返回了:

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

You need to tweak element name to get desired results though. 您需要调整元素名称以获得所需的结果。

Similar problem to me. 我也有类似的问题。 I wrote a custom JsonSerializer to generate different xml element name (read from @JsonTypeName) for each item in a collection. 我编写了一个自定义JsonSerializer来为集合中的每个项生成不同的xml元素名称(从@JsonTypeName读取)。

Here is my JsonSerializer: 这是我的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();
    }
}

for the following POJO (getters and constructors generated by lombok): 对于以下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;

}

and test code: 和测试代码:

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

this is output: 这是输出:

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

PS: I test my code with jackson version 2.9.3 PS:我用杰克逊版本2.9.3测试我的代码

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM