java 基于对象属性的 JAXB 元素名称

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4441692/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 06:23:48  来源:igfitidea点击:

JAXB element name based on object property

javajaxbquickbooks

提问by Vladimir

I have to create object model for following XMLs:

我必须为以下 XML 创建对象模型:

XML sample 1:

XML 示例 1:

<InvoiceAdd>
  <TxnDate>2009-01-21</TxnDate>
  <RefNumber>1</RefNumber>
  <InvoiceLineAdd>
  </InvoiceLineAdd>
</InvoiceAdd>

XML Sample 2:

XML 示例 2:

<SalesOrderAdd>
  <TxnDate>2009-01-21</TxnDate>
  <RefNumber>1</RefNumber>
  <SalesOrderLineAdd>
  </SalesOrderLineAdd>
</SalesOrderAdd>

The XML output will be based on a single string parameter or enum. String txnType = "Invoice"; (or "SalesOrder");

XML 输出将基于单个字符串参数或枚举。String txnType = "发票"; (或“销售订单”);

I would use single class TransactionAdd:

我会使用单类 TransactionAdd:

@XmlRootElement
public class TransactionAdd {  
  public String txnDate;
  public String refNumber;

  private String txnType;
  ...

  public List<LineAdd> lines;
}

instead of using subclasses or anything else. The code which creates the TransactionAdd instance is the same for both types of transaction it only differs on the type.

而不是使用子类或其他任何东西。创建 TransactionAdd 实例的代码对于两种类型的事务都是相同的,只是类型不同。

This XML is used by a rather known product called QuickBooks and is consumed by QuickBooks web service - so I can't change the XML, but I want to make it easy to be able to set element name based on property (txnType).

此 XML 由名为 QuickBooks 的知名产品使用,并由 QuickBooks Web 服务使用 - 因此我无法更改 XML,但我希望能够轻松设置基于属性 (txnType) 的元素名称。

I would consider something like a method to determine target element name:

我会考虑类似确定目标元素名称的方法:

@XmlRootElement
public class TransactionAdd {  
  public String txnDate;
  public String refNumber;

  private String txnType;
  ...

  public List<LineAdd> lines;

  public String getElementName() {
     return txnType + "Add";
  }
}

Different transactions will be created using following code:

将使用以下代码创建不同的交易:

t = new TransactionAdd();
t.txnDate = "2010-12-15";
t.refNumber = "123";
t.txnType = "Invoice";

The goal is to serialize t object with the top-level element name based on txnType. E.g.:

目标是使用基于 txnType 的顶级元素名称序列化 t 对象。例如:

<InvoiceAdd>
   <TxnDate>2009-01-21</TxnDate>
   <RefNumber>1</RefNumber>
</InvoiceAdd>

In case of t.txnType = "SalesOrder" the result should be

在 t.txnType = "SalesOrder" 的情况下,结果应该是

<SalesOrderAdd>
   <TxnDate>2009-01-21</TxnDate>
   <RefNumber>1</RefNumber>
</SalesOrderAdd>

At the moment I see only one workaround with subclasses InvoiceAdd and SalesOrderAdd and using @XmlElementRef annotation to have a name based on class name. But it will need to instantiate different classes based on transaction type and also will need to have two other different classes InvoiceLineAdd and SalesOrderLineAdd which looks rather ugly.

目前,我只看到一种解决方法,即使用子类 InvoiceAdd 和 SalesOrderAdd 并使用 @XmlElementRef 批注根据类名命名。但是它需要根据事务类型实例化不同的类,并且还需要有另外两个不同的类 InvoiceLineAdd 和 SalesOrderLineAdd,它们看起来相当难看。

Please suggest me any solution to handle this. I would consider something simple.

请给我建议任何解决方案来处理这个问题。我会考虑一些简单的事情。

采纳答案by bdoughan

To address the root element aspect you could do will need to leverage @XmlRegistry and @XmlElementDecl. This will give us multiple possible root elements for the TransactionAdd class:

为了解决您可以做的根元素方面,需要利用@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);
    }

}

Your TransactionAdd class will look something like the following. The interesting thing to note is that we will make the txnType property @XmlTransient.

您的 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;
    }

}

Then we need to supply a little logic outside the JAXB operation. For an unmarshal we will use the local part of the root element name to populate the txnType property. For a marshal we will use the value of the txnType property to create the appropriate JAXBElement.

然后我们需要在 JAXB 操作之外提供一些逻辑。对于解组,我们将使用根元素名称的本地部分来填充 txnType 属性。对于元帅,我们将使用 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);
    }

}

To Do

去做

I will look into addressing the lines property next.

接下来我将研究解决lines属性。

回答by bdoughan

You could use an XmlAdapter for this. Based on the String value of the txnType property you would have the XmlAdapter marshal an instance of an Object corresponding to InvoiceLineAdd or SalesOrderLineAdd.

您可以为此使用 XmlAdapter。根据 txnType 属性的字符串值,您可以让 XmlAdapter 封送与 InvoiceLineAdd 或 SalesOrderLineAdd 对应的对象实例。

This is how it would look:

这是它的外观:

TransactionAdd

交易添加

On the txnType property we will use a combination of @XmlJavaTypeAdapter and @XmlElementRef:

在 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;
    }

}

The adapted objects will look like:

调整后的对象将如下所示:

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 {

}

The XmlAdapter to convert between the String and the adapted objects will look like:

在 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();
    }

}

The following demo code can be used:

可以使用以下演示代码:

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);
    }

}

To produce/consume the following XML:

生成/使用以下 XML:

<transactionAdd>
    <salesOrderAdd/>
</transactionAdd>

For more information see:

有关更多信息,请参阅: