Spring REST同时支持JSON和XML

10

我希望提供一个全面的REST API,支持JSONXML,并且使用MappingJacksonHttpMessageConverterJaxbMarshaller分别在相同的模型上生成友好的JSONXML,但是这样做往往只能得到易读的XML或易读的JSON 1)

最佳的解决方案是什么?

1)由于像映射、根标记和关系等对象在jsonxml中被建模方式不同,因此需要以不同的方式设计要序列化的对象才能获得整洁的json和整洁的xml。诸如jaxb注释之类的工具只能做到这一点。


我可以想到几个候选方案

1)创建一个json和xml控制器/模型

public class Controller { 
   public Foo foo() { 
       return new Foo(); 
   } 
}

public class XmlController extends Controller {
   @Override
   public XmlFoo foo() { 
       return new new XmlFoo(super.foo()); 
   } 
}

public class JsonController extends Controller {
   @Override
   public JsonFoo foo() { 
       return new JsonFoo(super.foo()); 
   } 
}

给定一个模型对象Foo,创建一个JsonFooXmlFoo

2)编写自定义消息转换器

我尝试过这样做,结果有点复杂,因为视图必须知道如何将例如Foo解析为JsonFoo,以便能够将其序列化为可读格式。

3)让每个模型对象自己进行序列化,例如:

public class Foo {
    public String serialize(Serializer s) {
       return s.serialize(this);
    }
}

根据一些仲裁参数,让控制器注入正确的序列化器。

new Foo(new FooJsonSerializer());
new Foo(new FooXmlSerializer());

2
你能解释一下你所说的需要不同的设计是什么意思吗? - chrylis -cautiouslyoptimistic-
1
JAXB 例如可用于使用同一带注释的类从/到 XML 和 JSON 进行映射。无需不同的设计。 - user1907906
@chrylis,构建一个可以序列化为漂亮的JSON和漂亮的XML的复杂Java结构并不是一件简单的事情。 - Johan Sjöberg
1
@JohanSjöberg 啊,我没有从你的问题中得知你在询问嵌套/分层数据结构。在我看来,当你需要协调序列化和POJO的设计时,你已经到达了需要为对象创建自定义序列化器的地步。 - chrylis -cautiouslyoptimistic-
为什么每个人都在使用视图解析器?它们是为人类设计的。消息转换器则专门用于机器对机器通信。 - Michael-O
3个回答

6

在我的当前项目中,我没有使用ContentNegotiatingViewResolver来完成以下操作。对于控制器中的一个方法:

@RequestMapping(value = "/test", method = RequestMethod.GET)
@ResponseBody
public HttpEntity<BasicResponse> getBasicResponse() {
    return new HttpEntity<BasicResponse>(new BasicResponse());
}

我可以根据Accept请求头接收以下输出结果。 Accept:application/xml(需要在类路径上安装JAXB2)。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<basicResponse>
    <errors>
        <message>test1</message>
        <message>test2</message>
    </errors>
</basicResponse>

接受:application/json(需要在类路径上有Jackson)

{
    "errors" : ["test1", "test2"]
}

我的响应对象很简单,使用普通的注解:

package org.mypackage.response;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class BasicResponse {

    @XmlElementWrapper(name = "errors")
    @XmlElement(name = "message")
    private List<String> errors = new ArrayList<String>();

    public BasicResponse() {
        this.errors.add("test1");
        this.errors.add("test2");
    }

    public List<String> getErrors() {
        return errors;
    }

}
SpringSource Spring MVC Showcase项目 也是一个有用的资源。我认为他们将不同方法的转换分开了,但是我确实只针对一个方法进行了操作。
我不太清楚你的问题……但如果你想将输出序列化得更多,那么@chrylis是正确的,下一步应该是使用自定义序列化程序。但是,我遇到的所有问题(响应中的嵌套对象可能非常复杂)都可以完美转换为有效的XML或JSON。

谢谢您的回答,但我在问题表述上并不够清晰。我也可以获得有效的XML和JSON,但漂亮的XML API变成了丑陋的JSON API,反之亦然。我认为@chrylis说得很对。 - Johan Sjöberg

1

您应该使用ContentNegotiatingViewResolver

存在一个问题,即一些XML编组器无法正确映射POJO集合。XStream有解决方案(Moxy也有?)。

以下是一个开始的地方:

http://blog.springsource.org/2013/06/03/content-negotiation-using-views/

基本上,你需要使用MappingJacksonView和类似的XML视图,这是一个“假”视图,使用Jackson(或XML编组器)将您的POJO(s)编组为正确的格式。

服务器将根据以下之一发送正确的类型:

  • HTTP Accept标头
  • “文件类型扩展名”,例如“.json”
  • 查询字符串参数,例如“format=json”

0
关于省略字段,您可以使用注释@JsonIgnore(适用于Jackson)和/或@XStreamOmitField(适用于XStream)。
您尝试过这个吗:
@RequestMapping(value = "/{id}", 
method = RequestMethod.GET,
headers ={"Accept=application/json,application/xml"},
produces={"application/json", "application/xml"})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接