Java 接受/返回 XML/JSON 请求和响应 - Spring MVC

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35270660/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:29:27  来源:igfitidea点击:

Accepting / returning XML/JSON request and response - Spring MVC

javajsonxmlspringspring-mvc

提问by Mohan

I need to write a rest service which accepts XML/JSON as a input (POST method) and XML/JSON as a output (based on the input format). I have tried a below approach to achieve this but doesn't helped out.Endpoint method accepts both XML/JSON but while responding it always gives either JSON or XML based on the order specified in @RequestMapping -produces.Any help will be really appreciated.

我需要编写一个 rest 服务,它接受 XML/JSON 作为输入(POST 方法)和 XML/JSON 作为输出(基于输入格式)。我尝试了以下方法来实现这一点,但没有帮助。端点方法接受 XML/JSON,但在响应时,它总是根据 @RequestMapping -produces 中指定的顺序提供 JSON 或 XML。任何帮助将不胜感激.

My endpoint method:

我的端点方法:

@RequestMapping(value = "/getxmljson", method = RequestMethod.POST,produces={"application/json","application/xml"},
        consumes={"application/json", "application/xml"})
public @ResponseBody Student processXMLJsonRequest(@RequestBody Student student)
        throws Exception {
    System.out.println("*************Inside Controller");
    return student;
}

POJO Class: Student.java

POJO 类:Student.java

import java.io.Serializable;
import java.util.ArrayList;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import com.fasterxml.Hymanson.annotation.JsonIgnore;
import com.fasterxml.Hymanson.annotation.JsonPropertyOrder;

@XmlRootElement(name = "student")
@XmlType(propOrder = {"id", "name", "graduationTime", "courses"})
@JsonPropertyOrder({"id", "name", "graduationTime", "courses"})
public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private String graduationTime;
    private ArrayList<Course> courses = new ArrayList<Course>();

    @XmlElement
    public int getId() { return id; }
    @XmlElement
    public String getName() { return name; }
    @XmlElement
    public String getGraduationTime() { return graduationTime; }
    @XmlElement
    public ArrayList<Course> getCourses() { return courses; }

    public void setId(int value) { this.id = value; }
    public void setName(String value) { this.name = value; }
    public void setGraduationTime(String value) { this.graduationTime = value; }
    public void setCourses(ArrayList<Course> value) { this.courses = value; }

    @JsonIgnore
    public String toString() {
        return this.name + " - "
                + graduationTime == null? "Unknown" : graduationTime.toString();
    }

    public Student() {}
    public Student(int id, String name, String graduationTime) {
        this.id = id;
        this.name = name;
        this.graduationTime = graduationTime;
    }
}

POJO Class: Course.java

POJO 类:Course.java

import java.io.Serializable;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import com.fasterxml.Hymanson.annotation.JsonPropertyOrder;

@XmlRootElement(name = "course")
@XmlType(propOrder = {"courseName", "score"})
@JsonPropertyOrder({"courseName", "score"})
public class Course implements Serializable {
    private static final long serialVersionUID = 1L;

    private String courseName;
    private Integer score;

    public @XmlElement String getCourseName() { return courseName; }
    public @XmlElement Integer getScore() { return score; }

    public void setCourseName(String value) { courseName = value; }
    public void setScore(Integer value) { score = value; }

    public Course() {}
    public Course(String courseName, Integer score) {
        this.courseName = courseName;
        this.score = score;
    }
}

spring-config.xml

spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:sws="http://www.springframework.org/schema/web-services"
    xmlns:jee="http://www.springframework.org/schema/jee" xmlns:oxm="http://www.springframework.org/schema/oxm"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/web-services
        http://www.springframework.org/schema/web-services/web-services-2.0.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
       http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd
       http://www.springframework.org/schema/util
            http://www.springframework.org/schema/util/spring-util-2.5.xsd">

    <!-- DispatcherServlet Context: defines this servlet's request-processing 
        infrastructure -->

    <!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving 
        up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Configure to plugin JSON as request and response in method handler -->
    <beans:bean
        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <beans:property name="messageConverters">
            <beans:list>
                <beans:ref bean="jsonMessageConverter" />
                <beans:ref bean="xmlMessageConverter" />
            </beans:list>
        </beans:property>
    </beans:bean>

    <!-- Configure bean to convert JSON to POJO and vice versa -->
    <beans:bean id="jsonMessageConverter"
        class="org.springframework.http.converter.json.MappingHymanson2HttpMessageConverter">
    </beans:bean>

    <beans:bean id="xmlMessageConverter"
        class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter">
    </beans:bean>

    <beans:bean id="restTemplate" class="org.springframework.web.client.RestTemplate">

    </beans:bean>

    <beans:bean id="objectMapper" class="com.fasterxml.Hymanson.databind.ObjectMapper" />
    <context:component-scan base-package="com.test" />

</beans:beans>

Json Input:

JSON 输入:

{
"id":2014,
"name":"test",
"graduationtime":"09/05/2014",
"courses":[
{
"courseName":"Math",
"score":150
},
{
"courseName":"Che",
"score":150
}
]
}

XML Input:

XML 输入:

<?xml version="1.0" encoding="UTF-8" ?>
<student>
<id>2014</id>
<name>test</name>
<graduationTime>09/05/2014</graduationTime>
<courses>
    <courseName>Math</courseName>
    <score>150</score>
</courses>
<courses>
    <courseName>Che</courseName>
    <score>150</score>
</courses>
</student>

回答by Ali Dehghani

Register a filter that intercepts each request, warp the HttpServletRequestinto an implementation of HttpServletRequestWrapperand returns the Content-Typevalue for Acceptheader. For example, you can register a filter named SameInSameOutFilterlike following:

注册一个拦截每个请求的过滤器,将其HttpServletRequest转换为 的实现HttpServletRequestWrapper并返回标头的Content-TypeAccept。例如,您可以注册一个名称SameInSameOutFilter如下的过滤器:

@Component
public class SameInSameOutFilter extends GenericFilterBean {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        SameInSameOutRequest wrappedRequest = new SameInSameOutRequest((HttpServletRequest) request);
        chain.doFilter(wrappedRequest, response);
    }
}

It wraps current request in a SameInSameOutRequest:

它将当前请求包装在一个SameInSameOutRequest

public class SameInSameOutRequest extends HttpServletRequestWrapper {
    public SameInSameOutRequest(HttpServletRequest request) {
        super(request);
    }

    @Override
    public String getHeader(String name) {
        if (name.equalsIgnoreCase("accept")) {
            return getContentType();
        }

        return super.getHeader(name);
    }
}

This wrapper tells spring mvc to select a HttpMessageConverterbased on request's Content-Typevalue. If request body's Content-Typeis application/xml, then the response would be an XML. Otherwise, the response would be JSON.

这个包装器告诉 spring mvcHttpMessageConverter根据请求的Content-Type值选择一个。如果请求正文Content-Typeapplication/xml,则响应将是XML。否则,响应将是JSON

The other solution is to manually set the Acceptheader along with Content-Typein each request and avoid all these hacks.

另一种解决方案是在每个请求中手动设置Accept标头Content-Type并避免所有这些黑客攻击。

回答by manish

The best practice for handling different data formats with the same controller is to let the framework do all the work of figuring out the marshalling and unmarshalling mechanisms.

使用同一个控制器处理不同数据格式的最佳实践是让框架完成所有的工作,以确定编组和解组机制。

Step 1: Use minimal controller configuration

第 1 步:使用最少的控制器配置

@RequestMapping(value = "/getxmljson", method = RequestMethod.POST)
@ResponseBody
public Student processXMLJsonRequest(@RequestBody Student student) {
  return student;
}

There is no need to specify consumesand produceshere. As an example, consider that you may want this same method to handle other formats in the future such as Google Protocol Buffers, EDI, etc. Keeping the controllers free of consumesand produceswill let you add data formats through global configuration instead of having to modify the controller code.

没有必要在这里指定consumesproduces。例如,考虑到您将来可能希望使用相同的方法来处理其他格式,例如 Google Protocol Buffers、EDI 等。保持控制器免费,consumesproduces允许您通过全局配置添加数据格式,而不必修改控制器代码。

Step 2: Use ContentNegotiatingViewResolverinstead of RequestMappingHandlerAdapter

第 2 步:使用ContentNegotiatingViewResolver代替RequestMappingHandlerAdapter

  <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <property name="defaultViews">
      <list>
        <bean class="org.springframework.web.servlet.view.json.MappingHymanson2JsonView"/>
      </list>
    </property>
  </bean>

Let the view resolver decide how to read incoming data and how to write it back.

让视图解析器决定如何读取传入数据以及如何将其写回。

Step 3: Use Acceptsand Content-TypeHTTP headers

第 3 步:使用AcceptsContent-TypeHTTP 标头

Hitting your controller with the correct HTTP header values will force ContentNegotiatingViewResolverto marshal and unmarshal data automatically using the appropriate data representations.

使用正确的 HTTP 标头值命中您的控制器将强制ContentNegotiatingViewResolver使用适当的数据表示自动编组和解组数据。

If you want to exchange data in JSON format, set both headers to application/json. If you want XML instead, set both to application/xml.

如果要以 JSON 格式交换数据,请将两个标头都设置为application/json. 如果您想要 XML,请将两者都设置为application/xml.

If you do not want to use HTTP headers (which ideally you should), you can simply add .jsonor .xmlto the URL and ContentNegotiatingViewResolverwill do the rest.

如果您不想使用 HTTP 标头(理想情况下您应该这样做),您可以简单地将.json或添加.xml到 URL 并ContentNegotiatingViewResolver完成其余的工作。



You can check out my sample appthat I created using your code snippets that works fine for JSON and XML.

您可以查看使用适用于 JSON 和 XML 的代码片段创建的示例应用程序

回答by Gagandeep Kalra

Adding to Manish's answer above, if you don't wanna use xml based configuration use this java based configuration instead-

添加到上面 Manish 的答案,如果您不想使用基于 xml 的配置,请改用这个基于 java 的配置 -

@Bean
public ViewResolver contentNegotiatingViewResolver() {
    ContentNegotiatingViewResolver resolver =
            new ContentNegotiatingViewResolver();

    List<View> views = new ArrayList<>();
    views.add(new MappingHymanson2XmlView());
    views.add(new MappingHymanson2JsonView());

    resolver.setDefaultViews(views);
    return resolver;
}

回答by Sh4m

i was facing the same problem like yours. Below is my solution and sample.

我面临着和你一样的问题。以下是我的解决方案和示例。

Below is maven dependency that you need to include:

以下是您需要包含的 maven 依赖项:

        <dependency>
            <groupId>com.fasterxml.Hymanson.core</groupId>
            <artifactId>Hymanson-core</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.Hymanson.core</groupId>
            <artifactId>Hymanson-databind</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.Hymanson.core</groupId>
            <artifactId>Hymanson-annotations</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.Hymanson.dataformat</groupId>
            <artifactId>Hymanson-dataformat-xml</artifactId>
            <version>2.4.3</version>
        </dependency>

dispatcher-servlet.xml

调度程序-servlet.xml

<mvc:annotation-driven
        content-negotiation-manager="contentManager" />

<bean id="contentManager"
        class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
        <property name="favorPathExtension" value="false" />
        <property name="ignoreAcceptHeader" value="false" />
        <property name="defaultContentType" value="application/json" />
        <property name="useJaf" value="false" />
    </bean>

and my @RequestMapping ( you can use your own request mapping )

和我的@RequestMapping(你可以使用你自己的请求映射)

@RequestMapping(value = "/testXMLJSON",
            method = RequestMethod.GET, produces = {
                    MediaType.APPLICATION_XML_VALUE,
                    MediaType.APPLICATION_JSON_VALUE })
    @ResponseBody
    public ArtworkContentMessageType testXMLJSON()
    {
        //this is GS1 xml standard mapping.
        ArtworkContentMessageType resp = new ArtworkContentMessageType();
        StandardBusinessDocumentHeader standarBusinessDocumentHeader = new StandardBusinessDocumentHeader();
        resp.setStandardBusinessDocumentHeader(standarBusinessDocumentHeader );
        ArtworkContentType artWorkContent = new ArtworkContentType();
        resp.getArtworkContent().add(artWorkContent);

        return resp ;
    }

If application/xmlis required then, below headers must be present

如果application/xml需要,则必须存在以下标题

Content-Type:application/xml
Accept:application/xml

回答by Anusha Ruwanpathirana

If the resource define as below

如果资源定义如下

@GET
@Path("/{id}")
@Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public Student getStudent(@PathParam("id") String  id) {
 return student(); // logic to retunrs student object
}

Then the request should contains 'accept' header ('application/json' or application/xml'),
then it returns response in json or xml format.

然后请求应该包含'accept' 头('application/json' 或 application/xml'),
然后它以 json 或 xml 格式返回响应。

Sample request :

样品请求:

curl -k -X GET -H "accept: application/json" "https://172.17.0.5:8243/service/1.0/222"

Sample Student class

示例学生类

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;


@XmlRootElement(name = "student")
public class Student {
    private int id;
    private String name;
    private String collegeName;
    private int age;
    @XmlAttribute
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @XmlElement
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    @XmlElement
    public String getCollegeName() {
        return collegeName;
    }

    public void setCollegeName(String collegeName) {
        this.collegeName = collegeName;
    }

    public int getAge() {
        return age;
    }
    @XmlElement
    public void setAge(int age) {
        this.age = age;
    }

}