【问题标题】:JSON deserialisation failing (HTTP 400) with Jersey / MOXy使用 Jersey / MOXy 的 JSON 反序列化失败 (HTTP 400)
【发布时间】:2013-07-02 08:07:37
【问题描述】:

我有一个相对简单的 RESTful Web 服务,它使用 Jersey 和 Eclipselink MOXy。

如果数据格式为 XML,我可以向服务 POST 请求,但如果我将其发送为 JSON,服务器会生成 HTTP 400(错误请求),消息为:“客户端发送的请求是语法不正确。”。

服务端如下所示:

@POST
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public Subscription post(Subscription Subscription) {
    return Subscriptions.addSubscription(Subscription);
}

如果我在这样的网页中通过 Javascript 向它发送 XML 数据,就没有问题:

 $.ajax({
    url: 'http://localhost:8080/MyService/subscription',
type: 'POST',
    data:  "<subscription><notificationType>EMAIL</notificationType><notificationAddress>test@example.com</notificationAddress></subscription>",
    headers: { 
            Accept : "application/xml",
            "Content-Type": "application/xml"
    },
     // .. other params ... 
 );

但是,使用等效的 JSON,我得到 HTTP 400 - 错误请求:

 $.ajax({
    url: 'http://localhost:8080/MyService/subscription',
type: 'POST',
    data:  JSON.stringify(
                {subscription:{notificationType:"EMAIL",notificationAddress:"test@example.com"}}
            ),
    dataType: 'json'
    headers: { 
            Accept : "application/json",
            "Content-Type": "application/json"
     }
     // .. other params ... 
 );

我已经使用 Fiddler 检查了请求,数据格式和标头看起来都正确。

有趣的是,如果我将它插入到这段代码中,我可以成功地解组完全相同的 JSON 字符串:

String json = "{\"subscription\":{\"notificationType\":\"EMAIL\",\"notificationAddress\":\"test@example.com\"}}";
JAXBContext context = JAXBContext.newInstance(Subscription.class);
Unmarshaller m = context.createUnmarshaller();
m.setProperty("eclipselink.media-type", "application/json");
StringReader sr = new StringReader(json);
Subscription sub = (Subscription)m.unmarshal(sr);
System.out.println(sub.toString());

订阅类定义为:

@XmlRootElement(name="subscription")
public class Subscription {

    public enum NotificationType { EMAIL, SMS };

    private String notificationAddress;

    private NotificationType notificationType;

    public String getNotificationAddress() {
        return notificationAddress;
    }

    public void setNotificationAddress(String notificationAddress) {
        this.notificationAddress = notificationAddress;
    }
    public NotificationType getNotificationType() {
        return notificationType;
    }

    public void setNotificationType(NotificationType notificationType) {
        this.notificationType = notificationType;
    }
    public Subscription() {
    }

    @Override
    public String toString() {
        String s = "Subscription";
        if (getNotificationAddress() != null) {
            s += "(" + getNotificationType().toString() + ":" + getNotificationAddress() + ")";
        }
        return s;
    }
}

我已将 Eclipselink MOXy 配置为我的 JAXB 提供程序,方法是将此行添加到包含我的订阅者类的包中的 jaxb.properties:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

这似乎有效,至少对于编组从服务中发出的对象而言。

我错过了什么?

编辑: 这是我发布 JSON 数据时 Fiddler 捕获的内容:

POST http://localhost:8080/MyService/subscription HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Content-Length: 86
Accept: application/json
Origin: http://localhost:8080
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.116 Safari/537.36
Content-Type: application/json
Referer: http://localhost:8080/TestPage/AddSubscription.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-GB,en-US;q=0.8,en;q=0.6

{"subscription":{"notificationType":"EMAIL","notificationAddress":"test@example.com"}}

更新:

我从下面 Blaise 的回答中选择了 Option#2,创建了一个应用程序派生类,因此:

import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;

public class MyApplication  extends Application {
    @Override
    public Set<Class<?>> getClasses() {
        HashSet<Class<?>> set = new HashSet<Class<?>>(2);
        set.add(MOXyJsonProvider.class);
        set.add(SubscriptionResource.class); // the class containing my @POST service method.
        return set;
    }
 }

并添加到web.xml:

   <param-name>javax.ws.rs.Application</param-name>
   <param-value>com.example.MyApplication</param-value> 

现在我没有得到 HTTP 400,并且我的服务中的代码被命中,这是以前没有的,但是传入的 Subscription 对象具有所有未初始化的字段,例如通知地址为空。 如果我使用 XML 发布,它仍然可以正常工作。

更新#2:

我已将我的代码缩减为演示问题的最小子集,您可以在此处获取:

https://www.dropbox.com/sh/2a6iqw65ey0ahrk/D2ILi_722z

上面的链接包含一个带有 2 个 Eclipse 项目的 .zip; TestService(接受订阅对象的 Jersey RESTful 服务)和 TestPage(带有一些 JavaScript 的 .html 页面,用于以 JSON 或 XML 形式发布订阅对象)。

如果您在服务的 post 方法中设置断点,并使用测试页面发送 JSON 请求,您会看到传入了一个未初始化的订阅对象。

【问题讨论】:

    标签: java json jersey moxy


    【解决方案1】:

    EclipseLink JAXB (MOXy) 可以通过几种不同的方式与 Jersey 一起使用以生成 JSON。

    选项 #1 - 添加 jaxb.properties 文件

    Jersey 可以利用 JAXB (JSR-222) 实现来生成 JSON。如果您在域模型中添加jaxb.properties,则可以将 MOXy 指定为该提供程序。有一段时间,Jersey 中存在以下错误,阻止 MOXy 以这种方式使用,这可能就是您现在遇到的问题。

    这种生成 JSON 的方法有一些限制,可能不是您最终想要的。

    选项 #2 - 利用 MOXyJsonProvider

    从 EclipseLink 2.4.0 开始,MOXy 提供了自己的基于 JAXB 和 MOXy 元数据的 JSON 绑定。您可以使用 MOXyJsonProvider 进行配置。

    import java.util.*;
    import javax.ws.rs.core.Application;
    import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
    
    public class MyApplication  extends Application {
    
        @Override
        public Set<Class<?>> getClasses() {
            HashSet<Class<?>> set = new HashSet<Class<?>>(1);
            set.add(SubscriptionResource.class);
            return set;
        }
    
        @Override
        public Set<Object> getSingletons() {
            MOXyJsonProvider moxyJsonProvider = new MOXyJsonProvider();
            moxyJsonProvider.setIncludeRoot(true);
    
            HashSet<Object> set = new HashSet<Object>(1);
            set.add(moxyJsonProvider);
            return set;
        }
    
    } 
    

    更多信息

    这是我建议将 MOXy 与 Jersey 或任何其他 JAX-RS 提供程序一起使用的方式。

    【讨论】:

    • @DaveW - 由于您的 JSON 消息有一个根节点,您需要告诉 MOXyJsonProvider 包含根节点。我已经用一个示例 Application 类更新了我的答案。
    • 太棒了!这似乎奏效了。 (感谢您在这个问题和其他问题上提供的所有帮助 - 非常感谢)。
    猜你喜欢
    • 2014-10-08
    • 2014-12-18
    • 1970-01-01
    • 2013-11-16
    • 1970-01-01
    • 1970-01-01
    • 2015-05-07
    • 2011-06-14
    相关资源
    最近更新 更多