Estoy tratando de integrarme con un sistema de terceros y, según el tipo de objeto, cambia el elemento raíz del documento XML devuelto. Estoy usando la biblioteca JAXB para Marshalling / unmarshalling.
Root1:
<?xml version="1.0" encoding="UTF-8"?>
<root1 id='1'>
<MOBILE>9831138683</MOBILE>
<A>1</A>
<B>2</B>
</root1>
Root2:
<?xml version="1.0" encoding="UTF-8"?>
<root2 id='3'>
<MOBILE>9831138683</MOBILE>
<specific-attr1>1</specific-attr1>
<specific-attr2>2</specific-attr2>
</root2>
Estoy consumiendo todos los diferentes XML que los asignan a un objeto genérico :
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "ROW")
public class Row {
@XmlAttribute
private int id;
@XmlElement(name = "MOBILE")
private int mobileNo;
@XmlMixed
@XmlAnyElement
@XmlJavaTypeAdapter(MyMapAdapter.class)
private Map<String, String> otherElements;
}
Y el adaptador para convertir los valores desconocidos en un mapa :
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.parsers.DocumentBuilderFactory;
import java.util.HashMap;
import java.util.Map;
public class MyMapAdapter extends XmlAdapter<Element, Map<String, String>> {
private Map<String, String> hashMap = new HashMap<>();
@Override
public Element marshal(Map<String, String> map) throws Exception {
// expensive, but keeps the example simpler
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
Element root = document.createElement("dynamic-elements");
for(Map.Entry<String, String> entry : map.entrySet()) {
Element element = document.createElement(entry.getKey());
element.setTextContent(entry.getValue());
root.appendChild(element);
}
return root;
}
@Override
public Map<String, String> unmarshal(Element element) {
String tagName = element.getTagName();
String elementValue = element.getChildNodes().item(0).getNodeValue();
hashMap.put(tagName, elementValue);
return hashMap;
}
}
Esto pondrá la identificación y el número de móvil en los campos , y el resto, lo desconocido en un mapa .
Esto funciona si el elemento raíz está fijado a ROW como en el ejemplo anterior.
¿Cómo hacer que esto funcione de manera que el elemento raíz sea diferente en cada XML? ¿Una forma de ser agnóstico al elemento raíz mientras se desarma?
Respuestas:
Olvídate de JAXB para esto y analízalo tú mismo con StAX.
En el siguiente código, he cambiado el campo
mobileNo
deint
aString
, ya que el valor9831138683
es demasiado grande para unint
.Prueba
Salida
fuente
xml
vs unmarhshalling a través de JAXB (para objetos de datos conocidos donde se define el XSD), lo que sería mejor. Incluso si uso StAX, necesito volver a serializar elRow
objeto en xml adecuado con el elemento raíz correspondiente.Row
clase recuerde el nombre. --- Si necesita cuidar la entrada JSON, use un analizador JSON.mobileNo
una cadena. Incluso siint
fuera lo suficientemente grande como para almacenar un número de teléfono, el valor no representa una cantidad. En mi opinión, esto garantiza que se trate como una cadena incluso si el valor es numérico.No creo que haya una manera de hacer lo que pides. En XML, el nodo raíz (el documento) debe tener un elemento (o clase) definido. En otras palabras,
xs:any
solo funciona para subelementos. Incluso si hubiera una manera de lograr esto, en mi humilde opinión, esta es una mala decisión. En lugar de crear un elemento raíz variable ("dinámico"), debe agregar un atributo de nombre al mismo elemento para distinguir los archivos XML. Por ejemplo:Para esto, todo lo que necesita hacer es agregar un atributo de nombre a su elemento existente:
fuente
XmlRootElement
deRow
y unmarshalling aJAXBElement
través de:JAXBElement<Row> element = unmarshaller.unmarshal(new StreamSource(xml), Row.class); Row root = element.getValue();
. Esto proporciona el llenado de todos los campos y el consumo de XML con diferentes elementos raíz.xs:any
. Intenté esto con un esquema XML también. Si configura su elemento raíz para que sea básicamente cualquier cosa, su esquema se rompe. En otras palabras,xs:any
solo se puede usar en el contexto de un nodo hijo. Por último, debe ir a su cliente y mostrarle por qué esta es una mala implementación.XmlRootElement
deRow
funcionará cuando desarme. Mi pregunta para usted es, ¿cómo lo ordenaría si fuera necesario? Puede hacerlo con otros mecanismos, pero no con JAXB. La desventaja de este enfoque es que necesitará escribir sus propios validadores. Posible, pero pierde las capacidades nativas de JAXB para hacerlo.