2015-10-13 78 views
1

比方說,我有下面的類:JAXB:元帥/解組不同的XML元素/從一個類

@XmlRootElement 
class Payment { 
    @XmlElement 
    int amount; 
    @XmlElement 
    Currency currency; 

    @XmlElement 
    IResponse response; 
} 

如果response==null - 元素是一個「請求」,否則 - 元素是「響應」。當一個請求,元素應該(un)封送到(來自)根元素PaymentRequest,當一個響應 - (從)PaymentResponse

如何配置這樣的編組算法?如果JAXB無法做到,也許其他引擎可以?

回答

0

我終於通過截獲的StAX事件實施拆封。這裏是代碼:

JAXBContext jc = JAXBContext.newInstance(RootElement.class, A.class, B.class, C.class, D.class, E.class); 
Unmarshaller unmarsh = jc.createUnmarshaller(); 
XMLStreamReader xs = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(etalonRs)); 
XMLStreamReader xd = new StreamReaderDelegate(xs) { 
     public static final String ROOT_ELEMENT = "TestRoot"; 
     public static final int REPLACEABLE_LEVEL = 2; 
     public final Collection<String> sufficesToDelete = Arrays.asList("Rq", "Rs"); 

     protected Stack<String> elementNamesStack = new Stack<>(); 
     protected Set<String> replaceableNames = new HashSet<>(); 

     @Override 
     public String getLocalName() { 
      String realName = super.getLocalName(); 
      if (replaceableNames.contains(realName) && elementNamesStack.size() == REPLACEABLE_LEVEL) { 
       for (String suffix : sufficesToDelete) { 
        if (realName.endsWith(suffix)) { 
         return realName.substring(0, realName.lastIndexOf(suffix)); 
        } 
       } 
      } 
      return realName; 
     } 

     @Override 
     public int next() throws XMLStreamException { 
      final int eventCode = super.next(); 
     processLevel(eventCode); 
      return eventCode; 
     } 

     @Override 
     public int nextTag() throws XMLStreamException { 
      final int eventCode = super.nextTag(); 
      processLevel(eventCode); 
      return eventCode; 
     } 

     private void processLevel(int eventCode) { 
      switch (eventCode) { 
       case XMLStreamReader.START_ELEMENT: 
        final String origElementName = super.getLocalName(); 
        if ((elementNamesStack.size() + 1) == REPLACEABLE_LEVEL && elementNamesStack.peek().equals(ROOT_ELEMENT)) 
         replaceableNames.add(origElementName); 
        elementNamesStack.push(origElementName); 
        break; 
       case XMLStreamReader.END_ELEMENT: 
        assert(elementNamesStack.pop().equals(super.getLocalName())); 
        break; 

      } 
     } 
    }; 

Object o = unmarsh.unmarshal(xd); 

這裏是我的測試類。是的,在生產中的實際結構更復雜 - 有不同的「支付」和它們的元素都沒有根,所以我只好用@XmlAnyElement註釋:

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name = "TestRoot") 
public static class RootElement { 
    @XmlElement(name = "SomeDate") 
    private Date dt = new Date(); 

    @XmlAnyElement(lax=true) 
    private A a = new C(); 
} 

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name = "TestA") 
@XmlType 
public static abstract class A { 
    private int fld1 = 1; 

    @XmlAnyElement(lax=true) 
    @XmlElementWrapper 
    protected List<Object> list = new ArrayList<>(); 
} 

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name = "TestC") 
public static class C extends A { 
    private int fld2 = 3; 
} 

編組可以在執行同樣的方式,但你必須從頭開始編寫你的「StreamWriterDelegate」。

0

創建和編組這樣的根元素:

JAXBElement<Payment> jbe; 
if(payment.getResponse() != null){ 
    jbe = wrap(null, "PaymentResponse", payment); 
} else { 
    jbe = wrap(null, "PaymentRequest", payment); 
} 
m.marshal(jbe, sw); 

用簡單的輔助方法

<T> JAXBElement<T> wrap(String ns, String tag, T o){ 
    QName qtag = new QName(ns, tag); 
    Class<?> clazz = o.getClass(); 
    @SuppressWarnings("unchecked") 
    JAXBElement<T> jbe = new JAXBElement(qtag, clazz, o); 
    return jbe; 
} 

一種簡單的方法,使解組可能是創建兩個子類PaymentResponse和PaymentRequest充當@XmlRootElements。該的ObjectFactory包含

@XmlElementDecl(namespace = "", name = "PaymentRequest") 
public JAXBElement<PaymentRequest> 
createPaymentRequest(PaymentRequest value) { 
    return new JAXBElement<PaymentRequest>(_Payment_QNAME, PaymentRequest.class, null, value); 
} 
@XmlElementDecl(namespace = "", name = "PaymentResponse") 
public JAXBElement<PaymentResponse> 
createPaymentResponse(PaymentResponse value) { 
    return new JAXBElement<PaymentResponse>(_Payment_QNAME, PaymentResponse.class, null, value); 
} 

解組:

JAXBContext jc = JAXBContext.newInstance(PACKAGE); 
Unmarshaller m = jc.createUnmarshaller(); 
JAXBElement<?> tb = null; 
try { 
    Payment payment = readFrom(Payment.class); 
} catch(Exception e ){ 
} 

和方法readFrom:

public <T> T readFrom(Class<T> type) throws Exception { 
    try { 
     JAXBContext jc = JAXBContext.newInstance(PACKAGE); 
     Unmarshaller u = jc.createUnmarshaller(); 
     JAXBElement<T> jbe = (JAXBElement<T>)u.unmarshal(new File(XMLIN)); 
     return type.cast(jbe.getValue()); 
    } catch (JAXBException e) { 
     e.printStackTrace(); 
    } 
    return null; 
} 
+0

謝謝!不過,我在這裏看到兩個問題。在編組期間,您必須明確指定根元素的名稱。爲了實現反編組,你需要改變類的結構(通過爲請求和響應創建不同的類) - 不幸的是,我不能這樣做。所以,我找到了另一個解決方案,請看下一個答案。 –

+0

請求和響應的「不同類」不會影響應用程序的其餘部分,它們在別處不可見。 – laune