我必须为以下XML创建对象模型:
XML示例1:
<InvoiceAdd>
<TxnDate>2009-01-21</TxnDate>
<RefNumber>1</RefNumber>
<InvoiceLineAdd>
</InvoiceLineAdd>
</InvoiceAdd>
XML示例2:
<SalesOrderAdd>
<TxnDate>2009-01-21</TxnDate>
<RefNumber>1</RefNumber>
<SalesOrderLineAdd>
</SalesOrderLineAdd>
</SalesOrderAdd>
XML输出将基于单个字符串参数或枚举。 String txnType =“Invoice”; (或“SalesOrder”);
我会使用单一类TransactionAdd:
@XmlRootElement
public class TransactionAdd {
public String txnDate;
public String refNumber;
private String txnType;
...
public List<LineAdd> lines;
}
而不是使用子类或其他任何东西。创建TransactionAdd实例的代码对于两种类型的事务都是相同的,它只在类型上有所不同。
这个XML由一个名为QuickBooks的知名产品使用,并由QuickBooks Web服务使用 - 因此我无法更改XML,但我希望能够轻松地根据属性设置元素名称(txnType) )。
我会考虑类似于确定目标元素名称的方法:
@XmlRootElement
public class TransactionAdd {
public String txnDate;
public String refNumber;
private String txnType;
...
public List<LineAdd> lines;
public String getElementName() {
return txnType + "Add";
}
}
将使用以下代码创建不同的交易:
t = new TransactionAdd();
t.txnDate = "2010-12-15";
t.refNumber = "123";
t.txnType = "Invoice";
目标是使用基于txnType的顶级元素名称序列化t对象。 E.g:
<InvoiceAdd>
<TxnDate>2009-01-21</TxnDate>
<RefNumber>1</RefNumber>
</InvoiceAdd>
如果t.txnType =“SalesOrder”,结果应为
<SalesOrderAdd>
<TxnDate>2009-01-21</TxnDate>
<RefNumber>1</RefNumber>
</SalesOrderAdd>
目前我只看到一个使用子类InvoiceAdd和SalesOrderAdd的解决方法,并使用@XmlElementRef注释来获得基于类名的名称。但它需要根据事务类型实例化不同的类,并且还需要另外两个不同的类InvoiceLineAdd和SalesOrderLineAdd看起来相当丑陋。
请建议我处理此问题的任何解决方案。我会考虑一些简单的事情。
答案 0 :(得分:4)
您可以使用XmlAdapter。根据txnType属性的String值,您可以使用XmlAdapter封送与InvoiceLineAdd或SalesOrderLineAdd对应的Object实例。
它的外观如下:
<强> TransactionAdd 强>
在txnType属性上,我们将使用@XmlJavaTypeAdapter和@XmlElementRef的组合:
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class TransactionAdd {
private String txnType;
@XmlJavaTypeAdapter(MyAdapter.class)
@XmlElementRef
public String getTxnType() {
return txnType;
}
public void setTxnType(String txnType) {
this.txnType = txnType;
}
}
适应的对象看起来像:
<强> AbstractAdd 强>
import javax.xml.bind.annotation.XmlSeeAlso;
@XmlSeeAlso({InvoiceAdd.class, SalesOrderAdd.class})
public class AbstractAdd {
}
<强> InvoiceAdd 强>
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class InvoiceAdd extends AbstractAdd {
}
<强> SalesOrderAdd 强>
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class SalesOrderAdd extends AbstractAdd {
}
要在String和已调整对象之间进行转换的XmlAdapter将如下所示:
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class MyAdapter extends XmlAdapter<AbstractAdd, String> {
@Override
public String unmarshal(AbstractAdd v) throws Exception {
if(v instanceof SalesOrderAdd) {
return "salesOrderAdd";
}
return "invoiceAdd";
}
@Override
public AbstractAdd marshal(String v) throws Exception {
if("salesOrderAdd".equals(v)) {
return new SalesOrderAdd();
}
return new InvoiceAdd();
}
}
可以使用以下演示代码:
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(TransactionAdd.class);
File xml = new File("input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
TransactionAdd ta = (TransactionAdd) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(ta, System.out);
}
}
生成/使用以下XML:
<transactionAdd>
<salesOrderAdd/>
</transactionAdd>
有关详细信息,请参阅:
答案 1 :(得分:4)
要解决根元素方面的问题,您需要利用@XmlRegistry和@XmlElementDecl。这将为TransactionAdd类提供多个可能的根元素:
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
@XmlRegistry
public class ObjectFactory {
@XmlElementDecl(name="InvoiceAdd")
JAXBElement<TransactionAdd> createInvoiceAdd(TransactionAdd invoiceAdd) {
return new JAXBElement<TransactionAdd>(new QName("InvoiceAdd"), TransactionAdd.class, invoiceAdd);
}
@XmlElementDecl(name="SalesOrderAdd")
JAXBElement<TransactionAdd> createSalesOrderAdd(TransactionAdd salesOrderAdd) {
return new JAXBElement<TransactionAdd>(new QName("SalesOrderAdd"), TransactionAdd.class, salesOrderAdd);
}
}
您的TransactionAdd类将如下所示。有趣的是要注意我们将生成txnType属性@XmlTransient。
import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
public class TransactionAdd {
private String txnDate;
private String refNumber;
private String txnType;
private List<LineAdd> lines;
@XmlElement(name="TxnDate")
public String getTxnDate() {
return txnDate;
}
public void setTxnDate(String txnDate) {
this.txnDate = txnDate;
}
@XmlElement(name="RefNumber")
public String getRefNumber() {
return refNumber;
}
public void setRefNumber(String refNumber) {
this.refNumber = refNumber;
}
@XmlTransient
public String getTxnType() {
return txnType;
}
public void setTxnType(String txnType) {
this.txnType = txnType;
}
public List<LineAdd> getLines() {
return lines;
}
public void setLines(List<LineAdd> lines) {
this.lines = lines;
}
}
然后我们需要在JAXB操作之外提供一些逻辑。对于unmarshal,我们将使用根元素名称的本地部分来填充txnType属性。对于marshal,我们将使用txnType属性的值来创建适当的JAXBElement。
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(TransactionAdd.class, ObjectFactory.class);
File xml = new File("src/forum107/input1.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<TransactionAdd> je = (JAXBElement<TransactionAdd>) unmarshaller.unmarshal(xml);
TransactionAdd ta = je.getValue();
ta.setTxnType(je.getName().getLocalPart());
JAXBElement<TransactionAdd> jeOut;
if("InvoiceAdd".equals(ta.getTxnType())) {
jeOut = new ObjectFactory().createInvoiceAdd(ta);
} else {
jeOut = new ObjectFactory().createSalesOrderAdd(ta);
}
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(jeOut, System.out);
}
}
待办事项
我将研究下一步处理lines属性。