webentwicklung-frage-antwort-db.com.de

Es kann kein JSON-Array von Objekten mithilfe von Jersey Client aufgehoben werden

Ein JSON-Array mit einem Element, das ich entpacken möchte:

[
   {
      "id":"42",
      "status":"Active",
      "name":"purple monkey dishwasher"
   }
]

Die entsprechende Java-Klasse (Getter und Setter wurden aus Gründen der Übersichtlichkeit weggelassen):

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

Der Jersey-Client-Code, der eine HTTP-Anforderung stellt und angeblich ist, um die obige JSON in ein Ein-Element zu List<Foo> zu dekomprimieren:

Client client = Client.create();
WebResource apiRoot = client.resource("http://localhost:9000/api");
List<Badge> badges = apiRoot.path("/badges").get(new GenericType<List<Badge>>(){});

Die letzte Zeile, insbesondere der Aufruf von WebResource#get(), löst die folgende Ausnahme aus:

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)

Ich habe verschiedene Kombinationen von Anmerkungen zu Badge ausprobiert oder ein Array anstelle von GenericType verwendet:

List<Badge> badges = Arrays.asList(apiRoot.path("/badges").get(Badge[].class));

oder eine Zwischenvariable ClientResponse verwenden:

GenericType<List<Badge>> type = new GenericType<List<Badge>>(){};
ClientResponse clientResponse = apiRoot.path("/badges").get(ClientResponse.class);
List<Badge> badges = clientResponse.getEntity(type);

aber bisher hat das problem nicht gelöst.

Noch verwirrender ist die Tatsache, dass mein bestehendes Setup keine Probleme hat, die JSON-codierte Badges enthalten, die sich in anderen Strukturen befinden, wie zum Beispiel:

{
   "userid":"123456789",
   "userbadges":[
      {
         "badge":{
              "id":"42",
              "status":"Active",
              "name":"purple monkey dishwasher"
         },
         "earned":"2012-03-06 18:16:18.172"
      }
   ]
}

Was mache ich falsch?

26
Matt Ball

Ich konnte dieses Problem mit minimalem Aufwand lösen, indem ich JacksonJsonProvider als MessageBody(Reader|Writer)-Provider für die Jersey-Client-Instanz verwendet:

ClientConfig cfg = new DefaultClientConfig();
cfg.getClasses().add(JacksonJsonProvider.class);
Client client = Client.create(cfg);

Jacksons MessageBodyReader-Implementierung scheint sich besser zu verhalten als die Jersey-JSON-Implementierung.

Dank Wie kann ich die Serialisierung einer Liste von JAXB-Objekten in JSON anpassen? um mich in die Jackson-Richtung zu führen.

23
Matt Ball

Anmerkung: Ich bin der EclipseLink JAXB (MOXy) Leiter und Mitglied des JAXB (JSR-222) Expertengruppe.

Sie können die JSON-Bindungserweiterung verwenden, die der MOXy-Komponente in EclipseLink 2.4 hinzugefügt wird, um diesen Anwendungsfall zu behandeln:

Demo

Mit der Jersey-Client-API können Sie das gleiche MessageBodyReader/MessageBodyWriter auf der Serverseite auf der Clientseite nutzen.

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

Nachfolgend finden Sie ein allgemeines MessageBodyReader/MessageBodyWriter, das mit jedem Server/Client verwendet werden kann, um MOXy als JSON-Bindungsanbieter zu aktivieren.

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

}

Weitere Informationen


AKTUALISIEREN

In GlassFish 4 ist EclipseLink JAXB (MOXy) der von Jersey verwendete Standard-JSON-Bindungsanbieter:

13
bdoughan

Jersey verwendet standardmäßig JAXB für den (un) Marshalling-Prozess. Leider ist der JAXB-JSON-Prozessor kein Standard (Einelement-Arrays werden ignoriert. Leere Arrays werden in ein Ein-Element-leeres Array umgewandelt ... ).

Sie haben also zwei Möglichkeiten:

  1. konfigurieren von JAXB als Standard ( siehe hier für mehr);
  2. mit Jackson anstelle von JAXB - was ich empfehle.

Die Verwendung der Jackson-Clientseite erfolgt auf folgende Weise:

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>>() {});
11
yves amsellem

Ich hatte ein ähnliches Problem und wurde mit folgendem behoben

  1. Erstellen Sie einen JAXB-Kontextauflöser wie diesen

    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;
        }
    
    }
    
  2. Der Kontextauflöser wurde Ihrem Client hinzugefügt

    ClientConfig config = new DefaultClientConfig();
    config.getClasses().add(JAXBContextResolver.class);
    
    Client client = Client.create(config);
    
  3. Jetzt können Sie das Objektarray erhalten

    WebResource apiRoot = client.resource("http://localhost:9000/api");
    Badge[] badges = apiRoot.path("/badges").get(Badge[].class);
    

Und wenn Sie eine Liste wünschen, verwenden Sie einfach

   Arrays.asList(badges)
6
gurbieta

Importiere das

<dependency>
    <groupId>com.Sun.jersey</groupId>
    <artifactId>jersey-json</artifactId>
    <version>1.17</version>
    <scope>compile</scope>
</dependency>

und das ist der Code, den man aufheben muss

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);
}
1
user2116367

Dies kann mit einem Problem zusammenhängen, bei dem der Produzent eine Singleton-Liste nicht ordnungsgemäß in JSON codiert. In diesem Artikel finden Sie eine ausführlichere Erklärung und Lösungsvorschlag.

Basierend auf dem, was der Artikel beschreibt, und aufgrund der Fehlermeldung wird vermutlich Folgendes produziert:

{
   {
      "id":"42",
      "status":"Active",
      "name":"purple monkey dishwasher"
   }
}

Gemäß dem Artikel besteht die Lösung darin, den Provider zu erweitern und anzupassen, um zu korrigieren, wie Singleton-Listen und leere Listen in JSON formatiert werden.

Leider ist der Artikel in deutscher Sprache, den ich für mich selbst übersetzen musste - lassen Sie es mich wissen, wenn er Ihr Problem nicht wirklich anspricht. Wenn dies der Fall ist, geht dies an Dirk Dittmar, den Autor des Artikels.

PS: Wenn Sie die Seite mit Chrome wie ich übersetzen, stellen Sie sicher, dass Sie auf das Original zurückgreifen, um die Codeausschnitte zu sehen, da Teile davon irrtümlich in Whitespace "übersetzt" werden.

0
Paul Bellora