java - REST java 中的通用响应处理

标签 java rest jackson jaxb jersey

我已经创建了如下的 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); 
    }
}

我想使用附加参数动态创建此响应。后端返回一个 map ,其中将包含此附加 Prop 。

<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 服务器中运行。 我正在使用提供程序并使用 spring 和 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"

也有人可以建议如何解决上述问题吗? 提前致谢。

最佳答案

使用 JAXB 是不可能的,但是如果您想切换到使用 Jackson for XML ,那么你很容易做到这一点。您需要做的就是注释 Map@JsonAnyGetter 在你的 bean 中吸气.这将导致 Jackson 将映射键序列化为 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 for XML,您需要执行以下操作:

  1. 添加 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 版本。您要确保没有任何冲突版本。

  2. 您需要注册 JacksonJaxbXMLProvider与 Jersey

    public AppConfig extends ResourceConfig {
        public AppConfig() {
            register(JacksonJaxbXMLProvider,class);
        }
    }
    

完成这两件事后,它应该可以工作了。

将 Jackson 用于 XML 的好处在于,用于 JSON 的相同 Jackson 注释也可用于 XML,例如 @JsonProperty . Jackson 还理解 JAXB 注释(其中大部分)。因此,当您从 JAXB 迁移到 Jackson 时,您可能只保留相同的 JAXB 注释。至于 Prop 顺序,它不适用于动态属性,仅适用于已经定义的属性。

另见

  • 如果您想为 XML 配置 Jackson,请查看 this post ,我提到使用 ContextResolver<XmlMapper>

更新

如果您不能向您的项目添加任何新的依赖项,那么实现此目的的另一种方法是使用 org.w3c.dom 动态创建 XML API,它们是标准的 Java 类。它会更加冗长和更多的工作,但它会得到你想要的。参见 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);
        }
    }
}

关于java - REST java 中的通用响应处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51836068/

相关文章:

php - 如何使用 CSRF token 发布到 Laravel API?

java - 为什么 Jackson 多态序列化在列表中不起作用?

java - Groovy 类加载器不会加载 Java 类

java - 如果没有 catch block ,哪个异常处理程序将处理异常?

java - 使用 BITwise 运算实现二维数组

java - 通用抽象类的自定义 Jackson 反序列化

web-services - RESTful API 是否被过度使用/误用?

rest - 可以在 CF10 中使用 REST 代替 URL Rewrite 吗?

jackson - 谷歌管理SDK : Directory Users: How to get User phone numbers using Java client?

playframework-2.0 - 使用 Jackson Json Views 注释方法 Play Framework 2