【问题标题】:Jersey: JSON and StringMessageProvider clash, application/json methods generate invalid JSONJersey:JSON 和 StringMessageProvider 冲突,application/json 方法生成无效 JSON
【发布时间】:2014-10-08 07:21:55
【问题描述】:

我在 jetty+jersey 上有一个 jax-rs Web 服务,并尝试了不同的 JSON 消息提供程序(例如 Jackson 和 Gson)。 所有这些 POJO JSON 工作得很好,但是像这样的方法:

@GET
@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public String test() {
    return "This string should be \"quoted\" by message writer";
}

产生字符串

This string should be "quoted" by message writer

这当然不是有效的 JSON,会导致浏览器的 ajax 调用失败。 我希望它是

"This string should be \"quoted\" by message writer"

这种神奇的原因是 StringMessageProvider(基本类型的内部球衣提供者之一),它具有 / Produce/Consume 注释。而且我不想侵入球衣内部供应商。我想强制 jersey 首先使用 JSON 提供程序?

有什么想法吗?

【问题讨论】:

    标签: java json rest jersey jax-rs


    【解决方案1】:

    因此,在阅读了 jersey 文档并进行了一些调试后发现:

    • 是的,自定义消息读取器/写入器具有更高的优先级,但是
    • 在选择“合适的”供应商(从正确的供应商开始)后,通过自定义比较对其进行球衣排序,首先检查“类型”距离

    因此,具有方法返回类型“字符串”和 JSON 消息提供程序 MIME 类型通配符 (/),它通过比较字符串字符串的距离来检查第一个 StringMessageProvider(内置类型的默认球衣提供程序)

    通过添加非通用自定义消息提供者解决的问题,例如:

    public class StringProvider extends GsonProvider<String> {
        public StringProvider() {}
    }
    

    (其中GsonProvider implements MessageBodyReader&lt;T&gt;, MessageBodyWriter&lt;T&gt;)。

    在此返回的字符串被转义为正确的 JSON 并被浏览器的 ajax 调用处理程序识别之后。

    【讨论】:

    • 这很棒。无需使用 Gson。我通过添加一个实现 MessageBodyWriter 并使用 Jackson 进行 writeTo 实现的 StringMessageBodyWriter 来做同样的事情。
    【解决方案2】:

    您生成的 JSON 无效(您可能需要使用 JSONLint 对其进行验证)。

    所以,一个有效的 JSON 可以是:

    {"msg":"This string should be \"quoted\" by message writer"}
    
    ["This string should be \"quoted\" by message writer"]
    

    每个 JSON 的 Java 代码:

    @GET
    @Path("test")
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, String> test() {
        Map<String, String> map = new HashMap<String, String>();
        map.put("msg", "This string should be \"quoted\" by message writer");
        return map;
    }
    
    @GET
    @Path("test")
    @Produces(MediaType.APPLICATION_JSON)
    public Map<String, String> test() {
        return Arrays.asList("This string should be \"quoted\" by message writer");
    }
    

    注意:我将 RESTEasy 3.0.8 与 Jackson 2.4.1 一起使用

    【讨论】:

    • 对不起,你错了。 1) json.org 说:...“在 JSON 中,它们采用以下形式:... a) 对象是名称/值对的无序集合。... b) 数组是值的有序集合. ... c) 值可以是双引号中的字符串,也可以是数字,..."。您还可以查看最新的 JSON RFC tools.ietf.org/html/rfc7159,据说“字符串”值是有效的 JSON。
    • @tuxSlayer - 您返回的 原始字符串 作为 JSON 中的 顶级 对象无效。 Paul Vargas 返回一个 JSON 对象(字符串映射 -> 对象)。 RFC 7159 说字符串是有效的 JSON,但仅限于某些位置。单个原始字符串是无效的 JSON。
    • 让我们一起阅读吧 :) tools.ietf.org/html/rfc7159#page-4 JSON 文本是一个标记序列。标记集包括六个结构字符、字符串、数字和三个文字名称。 JSON 文本是一个序列化的值。请注意,某些先前的 JSON 规范将 JSON 文本限制为对象或数组。仅生成需要 JSON 文本的对象或数组的实现将是可互操作的,因为所有实现都将接受这些作为符合标准的 JSON 文本。 JSON-text = ws value ws,... 然后遵循“价值”定义)
    • 好吧,你说的是 7159,它是一个提议的标准——正如 RFC 本身所解释的那样:Obsoletes: 4627, 7158 March 2014。它只存在了一段时间几个月,您所谈论的库不太可能支持该标准。 previous 4627 标准不允许将原始字符串作为顶级,正如您的评论中特别提到的那样:请注意,JSON 的某些先前规范限制了 JSON 文本成为一个对象或一个数组。
    • 好的,同意。但是解决方案比我一开始更简洁和简单,并且比返回Map&lt;S, S&gt;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多