В наших приложениях существует довольно распространенная модель. Мы настраиваем настройку набора (или списка) объектов в Xml, которые реализуют общий интерфейс. При запуске приложение считывает Xml и использует JAXB для создания/настройки списка объектов. Я никогда не вычислял (после многократного чтения различных сообщений) "правильный путь", чтобы сделать это, используя только JAXB.
Например, у нас есть интерфейс Fee
и несколько конкретных классов реализации, которые имеют некоторые общие свойства, а также некоторые отклоняющиеся свойства и очень разные поведения. Xml, который мы используем для настройки списка сборов, используемых приложением:
<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>
В приведенном выше Xml каждый элемент <fee>
соответствует конкретному подклассу интерфейса Fee. Атрибут type
предоставляет информацию о том, какой тип должен быть создан, а затем, когда он создается, JMXB unmarshalling применяет свойства из оставшегося Xml.
Мне всегда приходится прибегать к подобным действиям:
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);
}
}
В приведенном выше коде JAXBConfigurator
представляет собой просто простую оболочку объектов JAXB для 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);
}
}
В конце вышеприведенного кода мы получаем список, который содержит типы, которые были настроены в Xml.
Есть ли способ заставить JAXB сделать это автоматически без необходимости писать код для повторения элементов, как указано выше?