Generando más json como json de JAXB y Jersey

Trabajo con un modelo de datos creado usando JAXB, a partir de eso puedo generar XML directamente

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>\
<metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#" xmlns:ext="http://musicbrainz.org/ns/ext#-2.0">
<artist-list offset="0" count="1">
   <artist ext:score="100" type="Group" id="4302e264-1cf0-4d1f-aca7-2a6f89e34b36">       
       <name>Farming Incident</name>
       <ipi-list>
          <ipi>1001</ipi>
       </ipi-list>
   </artist>
</artist-list>
</metadata>

y con la ayuda de Jersey también generar JSon usando notación Natural

"artist-list":
    {"offset":0,
     "count":1,
     "artist":[
         {"score":"100",
          "type":"Group",
          "id":"4302e264-1cf0-4d1faca7-2a6f89e34b36",
          "name":"Farming Incident",
          "ipi-list":
              {
                  "ipi":[
                       "1001"
                    ]
             }
          }]
     }

El Xml está bien, el json está casi bien, excepto que debido a que Json admite directamente matrices que tienen elementos como ipi-list y artist-list no parece muy json, ¿es posible generar más json como json a partir de mi modelo?

Información adicional según lo solicitado El json se genera a partir de este esquema MMD http://svn.musicbrainz.org/mmd-schema/trunk/brainz-mmd2-jaxb/src/main/resources/musicbrainz_mmd-2.0.xsd usando JAXB y Jersey, ver http://svn.musicbrainz.org/search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/mmd2/ResultsWriter.java y http://svn.musicbrainz.org/search_server/trunk/servlet/src/main/java/org/musicbrainz/search/servlet/mmd2/ArtistWriter.java

El punto es que quiero poder generar Json y XML a partir de un esquema con el mínimo de alboroto, pero aparentemente el Json no se ve bien, así que estoy buscando una manera de mejorarlo (realmente no tengo ninguna experiencia de json mismo)

preguntado el 22 de mayo de 12 a las 10:05

¿Su modelo está originalmente anotado en clases Java o en un esquema JAXB? -

Su salida parece un poco extraña. ¿Podría mostrar cómo se anotan las clases y cómo se convierte a XML/JSON? -

Es exactamente lo que tienes: para cada objeto tienes todas sus propiedades y así sucesivamente. Si no le gusta, haga sus propias anotaciones sobre las JAXB existentes para producir JSON personalizado con anotaciones específicas de JSON. -

He actualizado la pregunta -

No, es solo otro XmlElement, pero XmlElementWrapper suena interesante:

5 Respuestas

Nota: Soy el EclipseLink JAXB (MOXy) líder y miembro de la JAXB (JSR-222) grupo de expertos.

Puede aprovechar el enlace JSON y el documento de mapeo externo en EclipseLink JAXB (MOXy) para respaldar su caso de uso.

Archivo de mapeo externo (oxml.xml)

Puede utilizar el @XmlPath(".") extensión en MOXy para aplanar partes de su modelo de objeto. Especifique una ruta de "." le dice a MOXy que incluya el objeto al que se hace referencia en el nodo principal.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum10699038">
    <java-types>
        <java-type name="Metadata">
            <java-attributes>
                <xml-element java-attribute="artistList" xml-path="."/>
            </java-attributes>
        </java-type>
        <java-type name="Artist">
            <java-attributes>
                <xml-element java-attribute="ipiList" xml-path="."/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

jaxb.propiedades

Para especificar MOXy como su proveedor JAXB, debe agregar un archivo llamado jaxb.properties en el mismo paquete que su modelo de dominio con la siguiente entrada.

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

Demo

El siguiente código completa el modelo de objeto de su documento XML y luego se ordena a JSON. Demuestra cómo aprovechar el archivo de mapeo externo y poner MOXy en modo JSON.

package forum10699038;

import java.io.File;
import java.util.*;
import javax.xml.bind.*;

import org.eclipse.persistence.jaxb.JAXBContextProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        // READ FROM XML
        JAXBContext jcXML = JAXBContext.newInstance(Metadata.class);

        File xml = new File("src/forum10699038/input.xml");
        Unmarshaller unmarshaller = jcXML.createUnmarshaller();
        Metadata metadata = (Metadata) unmarshaller.unmarshal(xml);

        // WRITE TO JSON
        Map<String, Object> properties = new HashMap<String, Object>(3);
        properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum10699038/oxm.xml");
        properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
        properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
        JAXBContext jcJSON = JAXBContext.newInstance(new Class[] {Metadata.class}, properties);

        Marshaller marshaller = jcJSON.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(metadata, System.out);
    }

}

Salida

{
   "artist" : [ {
      "id" : "4302e264-1cf0-4d1f-aca7-2a6f89e34b36",
      "type" : "Group",
      "score" : "100",
      "name" : "Farming Incident",
      "ipi" : [ "1001" ]
   } ]
}

MOXY y Jersey

Puede usar fácilmente MOXy como su proveedor JSON en un entorno JAXB-RS como Jersey:


OTROS ARCHIVOS

A continuación se muestran las versiones de sus archivos que creé para asegurarme de que todo funcionara correctamente.

entrada.xml

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<metadata xmlns="http://musicbrainz.org/ns/mmd-2.0#" xmlns:ext="http://musicbrainz.org/ns/ext#-2.0">
    <artist-list offset="0" count="1">
        <artist ext:score="100" type="Group"
            id="4302e264-1cf0-4d1f-aca7-2a6f89e34b36">
            <name>Farming Incident</name>
            <ipi-list>
                <ipi>1001</ipi>
            </ipi-list>
        </artist>
    </artist-list>
</metadata>

metadatos

package forum10699038;

import javax.xml.bind.annotation.*;

@XmlRootElement
public class Metadata {

    @XmlElement(name="artist-list")
    ArtistList artistList;

}

Lista de artistas

package forum10699038;

import java.util.List;

public class ArtistList {

    private List<Artist> artist;

}

Artist

package forum10699038;

import javax.xml.bind.annotation.*;

@XmlType(propOrder={"name", "ipiList"})
public class Artist {

    @XmlAttribute
    private String id;

    @XmlAttribute
    private String type;

    @XmlAttribute(namespace="http://musicbrainz.org/ns/ext#-2.0")
    private String score;

    @XmlElement(name="ipi-list")
    private IPIList ipiList;

    private String name;

}

listaIPL

package forum10699038;

import java.util.List;

public class IPIList {

    private List<String> ipi;

}

información del paquete

@XmlSchema( 
    namespace = "http://musicbrainz.org/ns/mmd-2.0#", 
    elementFormDefault = XmlNsForm.QUALIFIED,
    xmlns={
        @XmlNs(prefix="", namespaceURI = "http://musicbrainz.org/ns/mmd-2.0#")
    }
) 
@XmlAccessorType(XmlAccessType.FIELD)
package forum10699038;

import javax.xml.bind.annotation.*;

Respondido el 13 de junio de 12 a las 11:06

Gracias por esta respuesta tan detallada, lo siento, debería haber respondido antes, pero no he entendido si hará lo que quiero. Ya tengo un modelo de objeto creado con Vanilla JAXB desde un XSD, ¿puedo usar este modelo de objeto con Moxy para generar mi Json deseado o tengo que crear otro modelo de objeto separado con Moxy para construir el Json? - Paul Taylor

@PaulTaylor: puede usar su modelo de objeto con MOXy. Creé uno para la respuesta para demostrar cómo podría verse. - bdoughan

Marcado como correcto, ¿es posible especificar javax.xml.bind.context.factory en el código en lugar de un archivo, ya que solo quiero usar Moxy al generar json, no al generar xml (porque no hay ningún problema allí y no quiero nada cambiar) - Paul Taylor

@PaulTaylor: si usa el MOXyJsonProvider entonces se usará MOXy para el enlace JSON y se usará otro proveedor JAXB para XML (ver blog.bdoughan.com/2012/05/…). Dado que MOXy implementa esa especificación JAXB (JSR-222), puede estar seguro de usarlo también para su XML. FYI WebLogic cambió a MOXy como el proveedor JAXB predeterminado: blog.bdoughan.com/2011/12/… - bdoughan

Estoy siguiendo su ejemplo, por lo que no uso MoxyJsonProvider (o JAX-RS), mi salida Xml ya está en producción, por lo que realmente preferiría no hacer nada que pueda cambiar la salida xml de ninguna manera. manera de especificar javax.xml.bind.context.factory.programmatically entre los dos. Moxy también está disponible en Maven. - Paul Taylor

El JSON creado por Jersey es una representación JSOON exacta de ese modelo proporcionado en el sitio. El problema que enfrenta aquí es que el sitio proporciona un modelo de datos incómodo, no que el marco no esté haciendo lo correcto.

¿Por qué este servicio devuelve un objeto de tipo lista de artistas en lugar de devolver una lista de artistas? ¿Por qué el servicio también tiene un objeto ipi-list? La verdadera pregunta que debería hacerse debería ser cómo se debe modelar esto para que funcione mejor con todas las tecnologías.

Respondido el 09 de junio de 12 a las 21:06

Entiendo su punto general, pero debe considerar que este modelo se genera a partir de un esquema xml, no puedo ver cómo dentro de xml puedo tener varios objetos ipi sin un objeto ipilist. es decir, debería ser posible tener un modelo que pueda generar xml y json sensibles - Paul Taylor

A mí personalmente no me gustan demasiado las anotaciones. Tenga la costumbre de generar JSON/XML en código sin formato. :)

Por ejemplo con Jackson (Gson también similar):

mapper = new ObjectMapper();
JsonNode root = mapper.createObjectNode();

JsonNode artist = mapper.createObjectNode();
artist.put("score", "100");
root.put("artist-list", artist);

ArrayNode ipiList = mapper.createArrayNode();
ipi.add("1001");
artist.put("ipi-list", ipiList);

Puede parecer mucho trabajo en la superficie. Pero para mí, esta es una forma muy clara de asignar JSON a objetos. Básicamente, tener el método toJson() en las clases de entidad es mi práctica habitual. Aquí hay un ejemplo: https://github.com/richardzcode/metrics/blob/master/src/main/java/com/rz/metrics/core/entities

Aunque soy solo yo.

Para JAXB, creo que debe anotar su entidad como se muestra a continuación:

@XmlElement(name = "ipi-list")
private List<Ipi> ipi;

Respondido el 11 de junio de 12 a las 22:06

Sí, es mucho trabajo, y significa que cada vez que cambia el modelo de objetos, tengo que actualizar la parte json del código. Prefiero trabajar con el modelo en sí y dejar que otra cosa se ocupe de la conversión a xml o json: Paul Taylor

Creo que el problema que está viendo con ipiList se debe a un problema con matrices/listas de un solo elemento en una biblioteca subyacente que Jersey usa para mapear a JSON.

La siguiente publicación de blog (no mía) muestra una forma de configurar Jersey, y los comentarios enlazan con formas alternativas (posiblemente más simples) de hacer que la representación JSON sea correcta para las matrices:

http://tugdualgrall.blogspot.co.uk/2011/09/jax-rs-jersey-and-single-element-arrays.html

Respondido el 11 de junio de 12 a las 23:06

Logré resolver la matriz JSON y el "error" de campo primitivo en la biblioteca JSON de Jersey. El ingrediente secreto es la magia JSONConfiguration y ContextResolver mencionada anteriormente. Vea mi siguiente publicación, tiene un ejemplo de código completo, ContextResolver personalizado y la clase de aplicación de descanso puede ser una lógica un tanto confusa a primera vista.

Cómo serializar primitivas de Java usando Jersey REST

  • json matriz para listas Java de elemento único o cero
  • campos enteros primitivos o booleanos sin caracteres de comillas

contestado el 23 de mayo de 17 a las 13:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.