我正试图解组的单元素JSON数组:
[
{
"id":"42",
"status":"Active",
"name":"purple monkey dishwasher"
}
]
相应的Java类(为简洁省略了getter& setter):
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Badge
{
@XmlElement(name="id")
private String id;
@XmlElement(name="status")
private Status status;
@XmlElement(name="name")
private String name;
public static enum Status
{
Active,
NotActive
}
}
发出HTTP请求且假定的Jersey客户端代码将上述JSON解组为单元素List<Foo>
:
Client client = Client.create();
WebResource apiRoot = client.resource("http://localhost:9000/api");
List<Badge> badges = apiRoot.path("/badges").get(new GenericType<List<Badge>>(){});
最后一行,特别是WebResource#get()
调用,抛出以下异常:
javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"status"). Expected elements are <{}badge>
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:662)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:258)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:253)
at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:120)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1063)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:498)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:480)
at com.sun.xml.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:75)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleStartElement(StAXStreamConnector.java:247)
at com.sun.xml.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:181)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:369)
at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:341)
at com.sun.jersey.core.provider.jaxb.AbstractListElementProvider.readFrom(AbstractListElementProvider.java:232)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:552)
at com.sun.jersey.api.client.ClientResponse.getEntity(ClientResponse.java:522)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:617)
at com.sun.jersey.api.client.WebResource.get(WebResource.java:191)
at com.redacted.badge.client.BadgerImpl.findAllBadges(BadgerImpl.java:105)
at com.redacted.webapp.admin.BadgeAction.unspecified(BadgeAction.java:40)
at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:245)
at org.apache.struts.actions.DispatchAction.execute(DispatchAction.java:170)
at org.apache.struts.chain.commands.servlet.ExecuteAction.execute(ExecuteAction.java:58)
at org.apache.struts.chain.commands.AbstractExecuteAction.execute(AbstractExecuteAction.java:67)
at org.apache.struts.chain.commands.ActionCommandBase.execute(ActionCommandBase.java:51)
at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:190)
at org.apache.commons.chain.generic.LookupCommand.execute(LookupCommand.java:304)
at org.apache.commons.chain.impl.ChainBase.execute(ChainBase.java:190)
at org.apache.struts.chain.ComposableRequestProcessor.process(ComposableRequestProcessor.java:283)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913)
at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.opensymphony.module.sitemesh.filter.PageFilter.parsePage(PageFilter.java:119)
at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:55)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.redacted.webapp.filter.MemberFilter.doFilter(MemberFilter.java:83)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.redacted.webapp.filter.AuthFilter.doFilter(AuthFilter.java:113)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.displaytag.filter.ResponseOverrideFilter.doFilter(ResponseOverrideFilter.java:125)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.redacted.webapp.filter.LanguageHandlingFilter.doFilter(LanguageHandlingFilter.java:151)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.redacted.webapp.filter.SetCharacterEncodingFilter.doFilter(SetCharacterEncodingFilter.java:146)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.redacted.webapp.filter.PartnerFilter.doFilter(PartnerFilter.java:59)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at com.redacted.webapp.filter.SessionStatusFilter.doFilter(SessionStatusFilter.java:113)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:470)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at com.googlecode.psiprobe.Tomcat60AgentValve.invoke(Tomcat60AgentValve.java:30)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
at java.lang.Thread.run(Thread.java:680)
我在Badge
上尝试了各种注释组合,或使用数组代替GenericType
:
List<Badge> badges = Arrays.asList(apiRoot.path("/badges").get(Badge[].class));
或使用中间ClientResponse
:
GenericType<List<Badge>> type = new GenericType<List<Badge>>(){};
ClientResponse clientResponse = apiRoot.path("/badges").get(ClientResponse.class);
List<Badge> badges = clientResponse.getEntity(type);
但到目前为止都没有解决问题。
更令人困惑的是,我现有的设置没有问题解组JSON编码的Badge
,这些都在其他结构中,如下所示:
{
"userid":"123456789",
"userbadges":[
{
"badge":{
"id":"42",
"status":"Active",
"name":"purple monkey dishwasher"
},
"earned":"2012-03-06 18:16:18.172"
}
]
}
我做错了什么?
答案 0 :(得分:23)
通过使用JacksonJsonProvider
作为Jersey客户端实例的MessageBody(Reader|Writer)
提供程序,我能够以最小的努力解决这个问题:
ClientConfig cfg = new DefaultClientConfig();
cfg.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cfg);
杰克逊的MessageBodyReader
实施似乎比泽西JSON的表现更为出色。
感谢How can I customize serialization of a list of JAXB objects to JSON?让我指向杰克逊的方向。
答案 1 :(得分:13)
注意:我是EclipseLink JAXB (MOXy)主管,是JAXB (JSR-222)专家组的成员。
您可以使用添加到EclipseLink 2.4中的MOXy组件的JSON Binding扩展来处理此用例:
<强>演示强>
Jersey客户端API允许您从客户端的服务器端利用相同的MessageBodyReader
/ MessageBodyWriter
。
package forum9627170;
import java.util.List;
import org.example.Customer;
import com.sun.jersey.api.client.*;
import com.sun.jersey.api.client.config.*;
public class Demo {
public static void main(String[] args) {
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(MOXyJSONProvider.class);
Client client = Client.create(cc);
WebResource apiRoot = client.resource("http://localhost:9000/api");
List<Badge> badges = apiRoot.path("/badges").accept("application/json").get(new GenericType<List<Badge>>(){});
for(Badge badge : badges) {
System.out.println(badge.getId());
}
}
}
<强> MOXyJSONProvider 强>
下面是一个通用的MessageBodyReader
/ MessageBodyWriter
,可以与任何服务器/客户端一起使用,以启用MOXy作为JSON绑定提供程序。
package forum9627170;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import javax.xml.transform.stream.StreamSource;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import javax.xml.bind.*;
@Provider
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class MOXyJSONProvider implements
MessageBodyReader<Object>, MessageBodyWriter<Object>{
@Context
protected Providers providers;
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
public Object readFrom(Class<Object> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream)
throws IOException, WebApplicationException {
try {
Class domainClass = getDomainClass(genericType);
Unmarshaller u = getJAXBContext(domainClass, mediaType).createUnmarshaller();
u.setProperty("eclipselink.media-type", mediaType.toString());
u.setProperty("eclipselink.json.include-root", false);
return u.unmarshal(new StreamSource(entityStream), domainClass).getValue();
} catch(JAXBException jaxbException) {
throw new WebApplicationException(jaxbException);
}
}
public boolean isWriteable(Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return true;
}
public void writeTo(Object object, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders,
OutputStream entityStream) throws IOException,
WebApplicationException {
try {
Marshaller m = getJAXBContext(getDomainClass(genericType), mediaType).createMarshaller();
m.setProperty("eclipselink.media-type", mediaType.toString());
m.setProperty("eclipselink.json.include-root", false);
m.marshal(object, entityStream);
} catch(JAXBException jaxbException) {
throw new WebApplicationException(jaxbException);
}
}
public long getSize(Object t, Class<?> type, Type genericType,
Annotation[] annotations, MediaType mediaType) {
return -1;
}
private JAXBContext getJAXBContext(Class<?> type, MediaType mediaType)
throws JAXBException {
ContextResolver<JAXBContext> resolver
= providers.getContextResolver(JAXBContext.class, mediaType);
JAXBContext jaxbContext;
if(null == resolver || null == (jaxbContext = resolver.getContext(type))) {
return JAXBContext.newInstance(type);
} else {
return jaxbContext;
}
}
private Class<?> getDomainClass(Type genericType) {
if(genericType instanceof Class) {
return (Class) genericType;
} else if(genericType instanceof ParameterizedType) {
return (Class) ((ParameterizedType) genericType).getActualTypeArguments()[0];
} else {
return null;
}
}
}
了解更多信息
在GlassFish 4中,EclipseLink JAXB(MOXy)是Jersey使用的默认JSON绑定提供程序:
答案 2 :(得分:11)
默认情况下,Jersey使用JAXB进行(un)编组过程,遗憾的是,JAXB JSON处理器不是标准处理器(单元素数组被忽略,空数组被转换为单元素空数组。 .. 的)。
所以,你有两个选择:
使用Jackson客户端以下列方式完成:
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
Client client = Client.create(clientConfig);
List<Badge> badges = client.resource("/badges").getEntity(new GenericType<List<Badge>>() {});
答案 3 :(得分:6)
我遇到了类似的问题,并通过以下
解决了制作一个像这样的JAXB上下文解析器
import java.util.ArrayList;
import java.util.List;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.JAXBContext;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext;
@Provider
public class JAXBContextResolver implements ContextResolver<JAXBContext> {
private JAXBContext context;
private Class<?>[] types = { Badge.class };
private List<Class<?>> classes = new ArrayList<Class<?>>();
public JAXBContextResolver() throws Exception {
this.context = new JSONJAXBContext(JSONConfiguration.natural().build(), types);
for (Class<?> clazz : types) {
classes.add(clazz);
}
}
public JAXBContext getContext(Class<?> objectType) {
return classes.contains(objectType) ? context : null;
}
}
为您的客户添加了上下文解析器
ClientConfig config = new DefaultClientConfig();
config.getClasses().add(JAXBContextResolver.class);
Client client = Client.create(config);
现在您可以获取对象数组
WebResource apiRoot = client.resource("http://localhost:9000/api");
Badge[] badges = apiRoot.path("/badges").get(Badge[].class);
如果你想要一个清单,只需使用
Arrays.asList(badges)
答案 4 :(得分:1)
导入此
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.17</version>
<scope>compile</scope>
</dependency>
这是unmarshall的代码
import com.sun.jersey.api.json.JSONJAXBContext;
import com.sun.jersey.api.json.JSONUnmarshaller;
public static <T> T unmarshalJson(String jsonTxt, Class<T> clazz) throws JAXBException {
JSONJAXBContext jctx = new JSONJAXBContext(clazz);
JSONUnmarshaller unm = jctx.createJSONUnmarshaller();
return (T)unm.unmarshalFromJSON(new StringReader(jsonTxt), clazz);
}
答案 5 :(得分:0)
这可能与生产者没有将单个列表正确编码为JSON的问题有关。有关更全面的解释和建议的解决方案,请参阅this article。
根据文章描述的内容,以及错误消息,我猜测正在生成以下内容:
{
{
"id":"42",
"status":"Active",
"name":"purple monkey dishwasher"
}
}
根据文章,解决方案在于扩展和自定义提供程序,以纠正单个列表和空列表如何格式化为JSON。
不幸的是这篇文章是用德语写的,我必须自己翻译一下 - 让我知道它是否真的无法解决你的问题。如果确实如此,可以归功于文章作者Dirk Dittmar。
PS - 如果你像我一样使用Chrome翻译页面,请确保切换回原始版本以查看代码段,因为它们的一部分被错误地“翻译”为空白。