W naszych aplikacjach występuje dość powszechny wzorzec. Konfigurujemy zestaw (lub listę) obiektów w Xml, które wszystkie implementują wspólny interfejs. Po uruchomieniu aplikacja odczytuje Xml i używa JAXB do tworzenia/konfigurowania listy obiektów. Nigdy nie rozgryzłem (po wielokrotnym czytaniu różnych postów) "właściwej drogi", aby to zrobić używając tylko JAXB.JAXB: jak wydzielać listę obiektów różnych typów, ale ze wspólnym rodzicem?
Na przykład mamy interfejs Fee
i wiele konkretnych klas implementacyjnych, które mają pewne wspólne właściwości, jak również niektóre właściwości rozbieżne i bardzo różne zachowania. XML używamy, aby skonfigurować listę opłat stosowanych przez aplikację jest:
<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>
W powyższym XML, każdy element <fee>
odpowiada konkretnej podklasy interfejsu Fee. Atrybut type
podaje informacje o tym, który typ należy utworzyć, a następnie po jego utworzeniu, JMBB zignoruje zastosowanie właściwości z pozostałego Xml.
zawsze muszę uciekać się do robienia czegoś takiego:
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);
}
}
W powyższym kodzie JAXBConfigurator
jest tylko prosty wrapper wokół JAXB obiektów dla 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);
}
}
Na koniec powyższego kodu otrzymamy listę zawierającą typy skonfigurowane w Xml.
Czy istnieje sposób, aby JAXB zrobił to automatycznie, bez konieczności pisania kodu do iterowania elementów, jak powyżej?
można zmieniać formatu XML czy też pracować z nim tak jak jest? – jtahlborn
@jtahlborn: Xml może być wszystkim, co można łatwo edytować ręcznie. –