2010-12-14 10 views
7

Я должен создать объектную модель для следующих XMLs:JAXB имя элемента на основе свойства объекта

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, поэтому я не могу изменить 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. Например:

<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, которые выглядят довольно уродливо.

Пожалуйста, предложите мне какое-либо решение для этого. Я бы подумал о чем-то простом.

ответ

3

Чтобы устранить аспект корневого элемента, который вы могли бы сделать, потребуется использовать @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); 
    } 

} 

делать

Я буду смотреть в решении свойство линии рядом.

+0

Большое спасибо! Похоже, он делает это сейчас. Я проверю свой проект и верну свои результаты! – Vladimir

3

Для этого вы можете использовать XmlAdapter. На основе значения String свойства txnType у вас должен быть маршал 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> 

Для получения дополнительной информации см:

+0

Отлично! Спасибо @Blaise, я прочитал ваш блог, но пропустил этот пост. Теперь я вижу, что это может мне помочь. Дайте еще одну небольшую заметку. Могу ли я использовать что-то вроде нового JAXBElement (новое QName («SalesOrderLineAdd»), LineAdd.class, lineAddObject) в качестве вывода метода маршала в адаптере? – Vladimir

+0

+1 для вашего блога :) –

+0

@Vladimir: Я обновил свой ответ с помощью примера кода.Хитрость заключается в том, чтобы совместно использовать @XmlJavaTypeAdapter и @XmlElementRef. –

Смежные вопросы