/ / JAXB y una pila de subclases produce json incorrectos - json, jaxb, moxy

JAXB y una pila de subclases producen json incorrectos - json, jaxb, moxy

¿Es posible lograr la desorganización de una estructura de clase que utiliza el anidamiento de varios abstract clases?
Dada una estructura de clase como esta:

@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Animal {}
public abstract class Mammal extends Animal {}
public class Tiger extends Mammal {}
public class Elephant extends Mammal {}

los @XmlRootElemented Zoo La clase tiene una lista de animales:

@XmlElementWrapper(name = "animals")
@XmlElements({
@XmlElement(name = "elephant", type = Elephant.class),
@XmlElement(name = "tiger", type = Tiger.class)
})
private List<Animal> animals;

Creo que tienes la idea ... el XML para esto:

<?xml version="1.0" encoding="utf-8"?>
<zoo>
<animals>
<tiger>
<name>Richard</name>
<furry>true</furry>
</tiger>
<elephant>
<name>Otis</name>
<furry>false</furry>
</elephant>
<tiger>
<name>Kirk</name>
<furry>true</furry>
</tiger>
</animals>
</zoo>

Esto se ve bien, genial.
Ahora el JSON ...

 {
"animals" : {
"tiger" : [ {
"name" : "Richard",
"furry" : true
}, {
"name" : "Kirk",
"furry" : true
} ],
"elephant" : [ {
"name" : "Otis",
"furry" : false
} ]
}
}

¿Por qué subgrupo el Mammal objetos de clase en JSON?

Estoy usando EclipseLink MOXy 2.6 para la clasificación.

Respuestas

1 para la respuesta № 1

RESPUESTA ORIGINAL

MOXy agrupa las teclas tiger y elephant para evitar repetirlos.


ACTUALIZACIÓN # 1

Por lo tanto, no es posible obtener un JSON como {"animales": [{"@type": "tigre"}, {"@tipo": elefante "}, ...]}?

Sí, es posible, solo necesita mapearlo de esa manera:

zoo

import java.util.List;
import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Zoo {

private List<Animal> animals;

}

Animal

import javax.xml.bind.annotation.*;

@XmlSeeAlso({Elephant.class, Tiger.class})
@XmlAccessorType(XmlAccessType.FIELD)
public abstract class Animal {

}

Manifestación

import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;

public class Demo {

public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
properties.put(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, "@");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Zoo.class}, properties);

Unmarshaller unmarshaller = jc.createUnmarshaller();
StreamSource json = new StreamSource("src/forum19384491/input.json");
Zoo zoo = unmarshaller.unmarshal(json, Zoo.class).getValue();

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

}

input.json/Output

{
"animals" : [ {
"@type" : "tiger"
}, {
"@type" : "elephant"
}, {
"@type" : "tiger"
} ]
}

ACTUALIZACIÓN # 2

Si desea mantener su representación XML actual y simplemente cambiar la representación JSON, puede usar la extensión de documento de mapeo externo de MOXy (consulte: http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html)

Documento de mapeo (oxm.xml)

Utilizaremos el documento de mapeo externo de MOXy para cambiar el mapeo para animals campo en el Zoo clase.

<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum19384491">
<java-types>
<java-type name="Zoo">
<java-attributes>
<xml-element java-attribute="animals"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>

Manifestación

En el código de demostración a continuación, creamos 2 instancias de JAXBContext en el mismo modelo de dominio. El de JSON aprovecha un documento de mapeo externo para personalizar el mapeo. input.xml es el documento XML de tu pregunta.

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 {
JAXBContext xmlJC = JAXBContext.newInstance(Zoo.class);

Unmarshaller unmarshaller = xmlJC.createUnmarshaller();
File xml = new File("src/forum19384491/input.xml");
Zoo zoo = (Zoo) unmarshaller.unmarshal(xml);

Map<String, Object> properties = new HashMap<String, Object>(4);
properties.put(JAXBContextProperties.OXM_METADATA_SOURCE, "forum19384491/oxm.xml");
properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
properties.put(JAXBContextProperties.JSON_ATTRIBUTE_PREFIX, "@");
JAXBContext jsonJC = JAXBContext.newInstance(new Class[] {Zoo.class}, properties);

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

}

Salida

A continuación se muestra el resultado de ejecutar el código de demostración.

{
"animals" : [ {
"@type" : "tiger",
"name" : "Richard",
"furry" : true
}, {
"@type" : "elephant",
"name" : "Otis",
"furry" : false
}, {
"@type" : "tiger",
"name" : "Kirk",
"furry" : true
} ]
}