unmarshal parse jaxbunmarshaller ejemplo java jaxb

jaxbunmarshaller - parse xml java jaxb



JAXB: ¿cómo separar una Lista de objetos de diferentes tipos pero con un elemento primario común? (3)

No creo que esto sea posible si todos los elementos se nombran <fee> . Incluso si fuera (o lo es) sería muy confuso desde el punto de vista del mantenimiento.

¿Tiene la capacidad de cambiar el nombre de varios elementos de tarifa según el tipo (por ejemplo, <tradeFee> lugar de <fee> )?

De lo contrario, puede crear una clase BaseFee que tenga todos los campos para cada tipo posible de <fee> . Puede deshacer los datos en una lista de objetos BaseFee y convertirlos en un tipo más específico en tiempo de ejecución, por ejemplo

List<BaseFee> fees = ...; for (BaseFee fee : fees) { if (isTradeFee(fee)) { TradeFee tradeFee = toTradeFee(fee); // do something with trade fee... } }

Un poco hack pero teniendo en cuenta los requisitos, debería hacer el trabajo.

Hay un patrón bastante común en nuestras aplicaciones. Configuramos una configuración de un conjunto (o lista) de objetos en Xml, que implementan una interfaz común. Al iniciar, la aplicación lee el Xml y utiliza JAXB para crear / configurar una Lista de objetos. Nunca he descubierto (después de leer varias publicaciones muchas veces) la "forma correcta" de hacerlo solo con JAXB.

Por ejemplo, tenemos una Fee interfaz y varias clases de implementación concretas que tienen algunas propiedades comunes, así como algunas propiedades divergentes y comportamientos muy diferentes. El Xml que utilizamos para configurar la Lista de tarifas utilizada por la aplicación es:

<fees> <fee type="Commission" name="commission" rate="0.000125" /> <fee type="FINRAPerShare" name="FINRA" rate="0.000119" /> <fee type="SEC" name="SEC" rate="0.0000224" /> <fee type="Route" name="ROUTES"> <routes> <route> <name>NYSE</name> <rates> <billing code="2" rate="-.0014" normalized="A" /> <billing code="1" rate=".0029" normalized="R" /> </rates> </route> </routes> ... </fee> </fees>

En el Xml anterior, cada elemento <fee> corresponde a una subclase concreta de una interfaz Fee. El atributo de type proporciona información sobre qué tipo crear una instancia y, una vez que se crea una instancia, el desemparejamiento de JAXB aplica las propiedades del Xml restante.

Siempre tengo que recurrir a hacer algo como esto:

private void addFees(TradeFeeCalculator calculator) throws Exception { NodeList feeElements = configDocument.getElementsByTagName("fee"); for (int i = 0; i < feeElements.getLength(); i++) { Element feeElement = (Element) feeElements.item(i); TradeFee fee = createFee(feeElement); calculator.add(fee); } } private TradeFee createFee(Element feeElement) { try { String type = feeElement.getAttribute("type"); LOG.info("createFee(): creating TradeFee for type=" + type); Class<?> clazz = getClassFromType(type); TradeFee fee = (TradeFee) JAXBConfigurator.createAndConfigure(clazz, feeElement); return fee; } catch (Exception e) { throw new RuntimeException("Trade Fees are misconfigured, xml which caused this=" + XmlUtils.toString(feeElement), e); } }

En el código anterior, el JAXBConfigurator es solo un contenedor simple alrededor de los objetos JAXB para desasignar:

public static Object createAndConfigure(Class<?> clazz, Node startNode) { try { JAXBContext context = JAXBContext.newInstance(clazz); Unmarshaller unmarshaller = context.createUnmarshaller(); @SuppressWarnings("rawtypes") JAXBElement configElement = unmarshaller.unmarshal(startNode, clazz); return configElement.getValue(); } catch (JAXBException e) { throw new RuntimeException(e); } }

Al final, del código anterior, obtenemos una lista que contiene los tipos que se configuraron en el Xml.

¿Hay alguna manera de hacer que JAXB haga esto automáticamente sin tener que escribir el código para repetir los elementos como se indicó anteriormente?


Puede usar un XmlAdapter para este caso de uso. La versión anterior maneja solo el tipo de Commission pero podría ampliarse fácilmente para admitir todos los tipos. AdaptedFee asegurarse de que AdaptedFee contenga las propiedades combinadas de todas las implementaciones de la interfaz de Fee .

import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.adapters.XmlAdapter; public class FeeAdapter extends XmlAdapter<FeeAdapter.AdaptedFee, Fee>{ public static class AdaptedFee { @XmlAttribute public String type; @XmlAttribute public String name; @XmlAttribute public String rate; } @Override public AdaptedFee marshal(Fee fee) throws Exception { AdaptedFee adaptedFee = new AdaptedFee(); if(fee instanceof Commission) { Commission commission = (Commission) fee; adaptedFee.type = "Commission"; adaptedFee.name = commission.name; adaptedFee.rate = commission.rate; } return adaptedFee; } @Override public Fee unmarshal(AdaptedFee adaptedFee) throws Exception { if("Commission".equals(adaptedFee.type)) { Commission commission = new Commission(); commission.name = adaptedFee.name; commission.rate = adaptedFee.rate; return commission; } return null; } }

Un XmlAdapter se configura utilizando la anotación @XmlJavaTypeAdapter :

import java.util.List; import javax.xml.bind.annotation.*; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Fees { @XmlElement(name="fee") @XmlJavaTypeAdapter(FeeAdapter.class) private List<Fee> fees; }

Para más información


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

Si está utilizando MOXy como su proveedor JAXB, puede usar la anotación @XmlPaths de @XmlPaths para extender la anotación JAXB @XmlElements estándar para hacer lo siguiente:

Matrícula

import java.util.List; import javax.xml.bind.annotation.*; import org.eclipse.persistence.oxm.annotations.*; @XmlRootElement public class Fees { @XmlElements({ @XmlElement(type=Commission.class), @XmlElement(type=FINRAPerShare.class), @XmlElement(type=SEC.class), @XmlElement(type=Route.class) }) @XmlPaths({ @XmlPath("fee[@type=''Commission'']"), @XmlPath("fee[@type=''FINRAPerShare'']"), @XmlPath("fee[@type=''SEC'']"), @XmlPath("fee[@type=''Route'']") }) private List<Fee> fees; }

Comisión

Las implementaciones de la interfaz de Fee se anotarán normalmente.

import javax.xml.bind.annotation.*; @XmlAccessorType(XmlAccessType.FIELD) public class Commission implements Fee { @XmlAttribute private String name; @XmlAttribute private String rate; }

Para más información