JAXB: comment démélanger une liste d'objets de types différents mais avec un parent commun?
Il y a un motif assez commun dans nos applications. Nous configurer une configuration d'un ensemble (ou une liste) des objets en Xml, ce qui implémentent une interface commune. Au démarrage, l'application lit le Xml et utilise JAXB pour créer/configurer une Liste d'objets. Je n'ai jamais compris (après lecture des différents posts à de nombreuses reprises) de la "bonne façon" de faire cela en utilisant seulement JAXB.
Par exemple, nous avons une interface Fee
et plusieurs de mise en œuvre concrète des classes qui ont en commun certaines propriétés, ainsi que quelques divergences de propriétés et des comportements très différents. Le Xml que nous utilisons pour configurer la Liste des Taxes utilisée par l'application est:
<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>
Dans le Xml ci-dessus, chaque <fee>
élément correspond à un béton sous-classe d'une Taxe de l'interface. Le type
attribut donne des informations sur le type d'instancier, et puis une fois qu'il est instancié, le JAXB unmarshalling applique les propriétés et le reste de Xml.
J'ai toujours recours à de faire quelque chose comme ceci:
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);
}
}
Dans le code ci-dessus, le JAXBConfigurator
est juste un simple wrapper autour de la JAXB objets pour unmarshalling:
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);
}
}
À la fin, le code ci-dessus, nous obtenons une Liste qui contient selon les types ont été configurés dans le fichier Xml.
Est-il un moyen d'obtenir JAXB pour faire cela automatiquement sans avoir à écrire le code pour parcourir les éléments ci-dessus?
source d'informationauteur Sam Goldberg
Vous devez vous connecter pour publier un commentaire.
Remarque: je suis le EclipseLink JAXB (MOXy) plomb et un membre de la JAXB (JSR-222) groupe d'experts.
Si vous utilisez MOXy que votre JAXB fournisseur, alors vous pourriez utiliser le MOXy est
@XmlPaths
annotation à étendre la norme JAXB@XmlElements
annotation pour effectuer les opérations suivantes:Frais
Commission
La mise en œuvre de la
Fee
interface serait annoté normalement.Pour Plus D'Informations
Vous pouvez utiliser un
XmlAdapter
pour ce cas d'utilisation. L'impl bleow gère juste laCommission
type, mais pourrait facilement être étendu pour prendre en charge tous les types. Vous devez vous assurer queAdaptedFee
contient les propriétés combinées de toutes les implémentations de laFee
interface.Un
XmlAdapter
est configuré à l'aide de la@XmlJavaTypeAdapter
annotation:Pour Plus D'Informations
Je ne pense pas que ce soit possible si tous les éléments sont nommés
<fee>
. Même si c'était(ou est), il serait très confuse de maintenance, de point de vue.Vous avez la possibilité de renommer les différents éléments des frais basés sur le type (par exemple,
<tradeFee>
au lieu de<fee>
)?Sinon, vous pouvez créer un
BaseFee
classe qui a tous les champs pour chaque type de<fee>
. Vous pouvez unmarshall des données dans une liste deBaseFee
objets et de les convertir dans un plus spécifiques de type à l'exécution, par exempleUn peu un hack mais étant donné les exigences auxquelles il doit faire le travail.