Java Jersey/MOXy的JSON反序列化失败(HTTP 400)

Java Jersey/MOXy的JSON反序列化失败(HTTP 400),java,json,jersey,moxy,Java,Json,Jersey,Moxy,我有一个相对简单的RESTful web服务,它使用Jersey和Eclipselink MOXy 如果数据格式为XML,我可以向服务发送请求,但是如果我将其改为JSON,服务器将生成HTTP 400(错误请求),并显示消息:“客户端发送的请求在语法上不正确。” 服务端如下所示: @POST @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_

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

如果数据格式为XML,我可以向服务发送请求,但是如果我将其改为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 ... 
 );
我已经使用Fiddler检查了请求,数据格式和标题看起来都是正确的

有趣的是,如果我将JSON字符串插入到下面的代码中,我就可以成功地解组完全相同的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
这似乎是可行的,至少对于编组从服务输出的对象来说是如此

我错过了什么

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

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下面的答案中选择了选项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;
    }
 }
import java.util.*;
导入javax.ws.rs.core.Application;
导入org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
公共类MyApplication扩展了应用程序{
@凌驾
public Set>Set=new HashSet可以通过几种不同的方式与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绑定

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

} 
import java.util.*;
导入javax.ws.rs.core.Application;
导入org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
公共类MyApplication扩展了应用程序{
@凌驾

公共集合>集合=新建HashSet@DaveW-由于您的JSON消息有一个根节点,您需要告诉
MOXyJsonProvider
以包含根节点。我已经用一个示例
应用程序
类更新了我的答案。太棒了!这似乎很管用。(感谢您在这个问题和其他问题上的所有帮助-非常感谢)。
   <param-name>javax.ws.rs.Application</param-name>
   <param-value>com.example.MyApplication</param-value> 
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;
    }

}