web-dev-qa-db-fra.com

Accepter / renvoyer une demande et une réponse XML / JSON - Spring MVC

J'ai besoin d'écrire un service de repos qui accepte XML/JSON comme entrée (méthode POST) et XML/JSON comme sortie (basé sur le format d'entrée). J'ai essayé une approche ci-dessous pour y parvenir, mais cela n'a pas aidé.La méthode Endpoint accepte à la fois XML/JSON mais tout en répondant, elle donne toujours JSON ou XML en fonction de l'ordre spécifié dans @RequestMapping -produces.Toute aide sera vraiment appréciée .

Ma méthode de point final:

@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;
}

Classe 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.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.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;
    }
}

Classe 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.jackson.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

<?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.MappingJackson2HttpMessageConverter">
    </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.jackson.databind.ObjectMapper" />
    <context:component-scan base-package="com.test" />

</beans:beans>

Entrée Json:

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

Entrée 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>
11
Mohan

La meilleure pratique pour gérer différents formats de données avec le même contrôleur est de laisser le framework faire tout le travail de définition des mécanismes de marshalling et de démarshalling.

Étape 1 : utilisez une configuration minimale du contrôleur

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

Il n'est pas nécessaire de spécifier consumes et produces ici. Par exemple, considérez que vous souhaiterez peut-être que cette même méthode gère à l'avenir d'autres formats tels que les tampons de protocole Google, EDI, etc. Garder les contrôleurs libres de consumes et produces vous permettra ajouter des formats de données via la configuration globale au lieu de devoir modifier le code du contrôleur.

Étape 2 : utilisez ContentNegotiatingViewResolver au lieu de RequestMappingHandlerAdapter

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

Laissez le résolveur de vue décider comment lire les données entrantes et comment les réécrire.

Étape 3 : utilisez Accepts et Content-Type En-têtes HTTP

Frapper votre contrôleur avec les valeurs d'en-tête HTTP correctes forcera ContentNegotiatingViewResolver à rassembler et dé-marshaler automatiquement les données en utilisant les représentations de données appropriées.

Si vous souhaitez échanger des données au format JSON, définissez les deux en-têtes sur application/json. Si vous préférez XML, définissez les deux sur application/xml.

Si vous ne souhaitez pas utiliser d'en-têtes HTTP (ce que vous devriez idéalement faire), vous pouvez simplement ajouter .json ou .xml à l'URL et ContentNegotiatingViewResolver fera le reste.


Vous pouvez vérifier mon exemple d'application que j'ai créé à l'aide de vos extraits de code qui fonctionne bien pour JSON et XML.

20
manish

Ajoutant à la réponse de Manish ci-dessus, si vous ne voulez pas utiliser la configuration basée sur xml, utilisez plutôt cette configuration Java basée sur la configuration-

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

    List<View> views = new ArrayList<>();
    views.add(new MappingJackson2XmlView());
    views.add(new MappingJackson2JsonView());

    resolver.setDefaultViews(views);
    return resolver;
}
9
Gagandeep Kalra

je faisais face au même problème que le vôtre. Voici ma solution et mon échantillon.

Vous trouverez ci-dessous la dépendance maven que vous devez inclure:

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

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

et mon @RequestMapping (vous pouvez utiliser votre propre mappage de demande)

@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 ;
    }

Si application/xml est alors requis, les en-têtes ci-dessous doivent être présents

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

Enregistrez un filtre qui intercepte chaque requête, déformez le HttpServletRequest dans une implémentation de HttpServletRequestWrapper et retourne le Content-Type valeur pour l'en-tête Accept. Par exemple, vous pouvez enregistrer un filtre nommé SameInSameOutFilter comme suit:

@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);
    }
}

Il enveloppe la demande actuelle dans un 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);
    }
}

Ce wrapper indique à Spring MVC de sélectionner un HttpMessageConverter en fonction du Content-Type valeur. Si le corps de la demande Content-Type est application/xml, alors la réponse serait un XML. Sinon, la réponse serait JSON.

L'autre solution consiste à définir manuellement l'en-tête Accept avec Content-Type dans chaque demande et évitez tous ces hacks.

0
Ali Dehghani

Si la ressource définit comme ci-dessous

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

Ensuite, la demande doit contenir l'en-tête "accept" ("application/json" ou application/xml "),
puis il renvoie une réponse au format json ou xml.

Demande d'échantillon :

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

Exemple de classe d'élève

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

}
0