8

次の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出力は、単一の文字列パラメーターまたは列挙型に基づいています。文字列txnType="請求書"; (または「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オブジェクトをシリアル化することです。例えば:

<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アノテーションを使用してクラス名に基づいた名前を付ける1つの回避策しかありません。ただし、トランザクションタイプに基づいて異なるクラスをインスタンス化する必要があります。また、見た目がかなり醜い2つの異なるクラスInvoiceLineAddとSalesOrderLineAddが必要になります。

これを処理するための解決策を教えてください。簡単なことを考えます。

4

2 に答える 2

4

これにはXmlAdapterを使用できます。txnTypeプロパティのString値に基づいて、XmlAdapterはInvoiceLineAddまたはSalesOrderLineAddに対応するオブジェクトのインスタンスをマーシャリングします。

これはどのように見えるかです:

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 {

}

文字列と適応オブジェクトの間で変換する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>

詳細については、以下を参照してください。

于 2010-12-14T16:55:33.303 に答える
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操作の外部に小さなロジックを提供する必要があります。アンマーシャルの場合、ルート要素名のローカル部分を使用して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);
    }

}

やること

次に、linesプロパティのアドレス指定について検討します。

于 2010-12-15T16:20:03.137 に答える