我创建了如下的REST响应
@XmlRootElement(name = "customer_info")
@JsonIgnoreProperties(value = { "map" })
public class Customer {
private String name;
private Integer id;
private long time;
private Map<String, Object> map = new TreeMap<>();
@JsonAnyGetter
public Map<String, Object> getMap() {
return map;
}
@JsonAnySetter
public void setMap(String name, Object values) {
this.map.put(name, values);
}
}
我想动态创建带有附加参数的响应。后端返回一个包含此附加道具的地图。
<customer_info>
<name></name>
<id></id>
<!--The below will have dynamic prop based on key-->
<dynamic1></dynamic1>
<dynamic2></dynamic2>
</customer_info>
应用程序配置
ResourceConfig config = new DefaultResourceConfig();
config.add(resources);
Map<String, MediaType> type = config.getMediaTypeMappings()
type.put("json", MediaType.APPLICATION_JSON_TYPE);
type.put("xml", MediaType.APPLICATION_XML_TYPE);
servletHandler.addServlet(new ServletHolder(new ServletContainer(config)), "/*");
当我添加Map时,xml结构如下所示,这是不正确的。我需要上面的xml结构。有人可以指导我如何实现这一点吗?
此外,XML元素的名称应与地图中的键相同。JSON响应正常,但xml结构不正确。
<customer_info>
<name></name>
<id></id>
<!--The below will have dynamic prop based on key-->
<map>
<entry>
<key>dummy_param1</key>
<value>11</value>
</entry>
<entry>
<key>dummy_param2</key>
<value>10</value>
</entry>
<map>
</customer_info>
我正在使用Jersey作为REST框架,并在Jetty服务器中运行。 我正在使用提供程序,并通过springs和REST服务对其进行注册。
<bean class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider"/>
<bean class="com.dummy.CustomerService"/>
<bean class="com.dummy.Service2"/>
<bean class="com.dummy.Service3"/>
库gradle配置如下:
"org.codehaus.jackson:jackson-core-asl:1.9.2",
"org.codehaus.jackson:jackson-jaxrs:1.9.2",
"org.codehaus.jackson:jackson-mapper-asl:1.9.2",
"org.codehaus.jackson:jackson-xc:1.9.2",
"com.sun.jersey:jersey-client:1.12",
"com.sun.jersey:jersey-core:1.12",
"com.sun.jersey:jersey-json:1.12",
"com.sun.jersey:jersey-server:1.12",
"com.sun.jersey:jersey-servlet:1.12",
"org.codehaus.jettison:jettison:1.1",
"javax.ws.rs:jsr311-api:1.1.1",
"com.sun.jersey.contribs:jersey-apache-client:1.12",
"com.sun.jersey.contribs:jersey-apache-client4:1.12",
"com.sun.jersey.contribs:jersey-multipart:1.12",
"com.sun.jersey:jersey-client:1.12",
"com.sun.jersey:jersey-core:1.12",
"com.sun.jersey:jersey-json:1.12",
"javax.ws.rs:jsr311-api:1.1.1",
"org.codehaus.jackson:jackson-core-asl:1.9.2",
"org.codehaus.jackson:jackson-jaxrs:1.9.2",
"org.codehaus.jackson:jackson-mapper-asl:1.9.2",
"org.codehaus.jackson:jackson-xc:1.9.2",
"javax.servlet:javax.servlet-api:3.1.0",
"org.eclipse.jetty:ecs-jetty-server:9.4.0.v20161208",
"org.eclipse.jetty:jetty-util:9.4.0.v20161208",
"org.eclipse.jetty:jetty-servlet:9.4.0.v20161208",
"org.eclipse.jetty:jetty-servlets:9.4.0.v20161208",
"org.eclipse.jetty:jetty-http:9.4.0.v20161208",
"org.eclipse.jetty:jetty-security:9.4.0.v20161208",
"org.eclipse.jetty:jetty-io:9.4.0.v20161208",
"org.eclipse.jetty:jetty-continuation:9.4.0.v20161208",
"org.eclipse.jetty:jetty-deploy:9.4.0.v20161208",
"org.eclipse.jetty:jetty-webapp:9.4.0.v20161208",
"org.eclipse.jetty:jetty-xml:9.4.0.v20161208"
也可以有人建议如何解决以上问题吗? 预先感谢。
答案 0 :(得分:4)
JAXB无法实现,但是如果要切换为使用Jackson for XML,则可以轻松实现。您需要做的就是用Map
注释bean中的@JsonAnyGetter
getter。这将导致Jackson将Map键序列化为XML中的普通元素。这是一个测试
@Path("jackson-xml")
public class JacksonXmlResource {
@GET
@Produces("application/xml")
public Response get() {
Model model = new Model();
model.setProp("foo", "bar");
model.setProp("name", "Paul");
return Response.ok(model).build();
}
public static class Model {
private Map<String, Object> props = new HashMap<>();
@JsonAnyGetter
public Map<String, Object> getProps() {
return this.props;
}
@JsonAnySetter
public void setProp(String key, Object value) {
this.props.put(key, value);
}
}
}
这将导致以下XML
<Model>
<foo>bar</foo>
<name>Paul</name>
</Model>
要使用Jackson的XML,您需要执行以下操作:
添加Jackson依赖项并删除(排除)JAXB提供程序
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<exclusions>
<exclusion>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-jaxb</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-xml-provider</artifactId>
<version>${jackson2.version}</version>
</dependency>
您应该从项目中应该已经拥有的jersey-container-servlet
依赖项中排除JAXB提供程序。 ${jackson2.version}
应该是您项目中当前使用的任何版本的Jackson。如果您没有对Jackson的任何明确依赖关系,但使用的是jersey-media-json-jackson
,请找出引入的Jackson版本。您要确保没有任何冲突的版本。
您需要在泽西岛注册JacksonJaxbXMLProvider
public AppConfig extends ResourceConfig {
public AppConfig() {
register(JacksonJaxbXMLProvider,class);
}
}
完成这两项操作后,它应该可以工作。
使用Jackson进行XML的好处是,您用于JSON的相同Jackson注释也可以用于XML,例如@JsonProperty
。杰克逊也了解JAXB批注(大多数)。因此,当您从JAXB迁移到Jackson时,您可能只需保留JAXB批注相同。就prop顺序而言,它不适用于动态属性,仅适用于已定义的属性。
ContextResolver<XmlMapper>
如果您不能向项目添加任何新的依赖项,那么另一种实现方法是使用org.w3c.dom
API(这是标准Java类)来动态创建XML。这将更加冗长,需要更多工作,但是它将为您提供所需的东西。有关某些说明,请参见this post。这是一个例子。添加注释后应该很容易。
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
@Path("dom-api")
public class DomXmlResource {
@GET
@Produces("application/xml")
public Response getXml() throws Exception {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
Document doc = docBuilder.newDocument();
// create root element
Element rootEl = doc.createElement("Model");
doc.appendChild(rootEl);
Model model = new Model();
model.setProp("foo", "bar");
model.setProp("name", "Paul");
model.setValue("FooBar");
// set static defined properties
Element valueEl = doc.createElement("value");
valueEl.appendChild(doc.createTextNode(model.getValue()));
rootEl.appendChild(valueEl);
// set dynamic properties
for (Map.Entry<String, Object> entry: model.getProps().entrySet()) {
Element dynamicEl = doc.createElement(entry.getKey());
dynamicEl.appendChild(doc.createTextNode(String.valueOf(entry.getValue())));
rootEl.appendChild(dynamicEl);
}
// return StreamingOutput so we can just stream the
// XML results without having to store the String into memory.
StreamingOutput entity = new StreamingOutput() {
@Override
public void write(OutputStream out)
throws IOException, WebApplicationException {
try {
// write the XML structure to the output stream.
Transformer transformer = TransformerFactory.newInstance()
.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
StreamResult result = new StreamResult(out);
DOMSource source = new DOMSource(doc);
transformer.transform(source, result);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
};
return Response.ok(entity).build();
}
public static class Model {
private String value;
private Map<String, Object> props = new HashMap<>();
public String getValue() {
return this.value;
}
public void setValue(String value) {
this.value = value;
}
public Map<String, Object> getProps() {
return this.props;
}
public void setProp(String key, Object value) {
this.props.put(key, value);
}
}
}