2015-10-13 56 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