2017-09-27 30 views
2

我有一個包含一個稱爲Xml element (name = "Header")Xml document (name = "Entity")和未知名的另一個XML元素,但已知的是,具有內部XmlElement(name="label")JAXB元素與特定的類型,但未知名

以下是可能的XMLS:

<Entity> 
    <Header>this is a header</Header> 
    <a> 
    <label>this is element A</label> 
    <otherElements/> 
    </a> 
</Entity> 

<Entity> 
    <Header>this is a different header</Header> 
    <b> 
    <label>this is some other element of name b</label> 
    <others/> 
    </b> 
</Entity> 

這裏是我的JAXB註釋類:

@XmlRootElement(name = "Entity") 
@XmlAccessorType(XmlAccessType.NONE) 
public class Entity { 
    @XmlElement(name = "Header") 
    private Header header; 

    @XmlElements({ 
     @XmlElement(name = "a", type=LabelledElement.A.class), 
     @XmlElement(name = "b", type=LabelledElement.B.class) 
    }) 
    private LabelledElement labelledElement; 

    // constructors, getters, setters... 
} 

@XmlAccessorType(XmlAccessType.NONE) 
public abstract class LabelledElement { 
    @XmlElement 
    private String label; 
    @XmlAnyElement 
    private List<Element> otherElements; 

    public static class A extends LabelledElement {} 
    public static class B extends LabelledElement {} 
} 

這是偉大的工作!但後來我發現,它不僅<a><b>

這可能是<c><asd>甚至<anything> ...

所以上市XmlElement(name = "xyz", type = LabelledElement.xyz.class)顯然不是正確的解決方案。

無論LabelledElement名稱是什麼,我所關心的都是Entity#getLabelledElement()#getLabel()

這甚至可以用JAXB嗎?

回答

1

用的EclipseLink JAXB實現(莫西),這應工作:

@XmlRootElement(name = "Entity") 
@XmlSeeAlso({LabelledElement.class}) //Might not be necessary 
@XmlAccessorType(XmlAccessType.NONE) 
public class Entity { 
    @XmlElement(name = "Header") 
    private Header header; 

    @XmlPath("child::*[position() = 2]") 
    @XmlJavaTypeAdapter(MapAdapter.class) 
    private Map<String,LabelledElement> labelledElementMap; 


    public LabelledElement getLabelledElement(){ 
     return labelledElementMap.values().get(0); 
    } 
    // constructors, getters, setters... 
} 

MapAdapter類:

public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, LabelledElement>> { 

    public static class AdaptedMap { 

     @XmlVariableNode("key") 
     List<LabbeledElement> entries = new ArrayList<LabbeledElement>(); 

    } 

    public static class AdaptedEntry { 

     @XmlTransient 
     public String key; 

     @XmlElement 
     public LabelledElement value; 

    } 

    @Override 
    public AdaptedMap marshal(Map<String, LabelledElement> map) throws Exception { 
     AdaptedMap adaptedMap = new AdaptedMap(); 
     for(Entry<String, LabelledElement> entry : map.entrySet()) { 
      AdaptedEntry adaptedEntry = new AdaptedEntry(); 
      adaptedEntry.key = entry.getKey(); 
      adaptedEntry.value = entry.getValue(); 
      adaptedMap.entries.add(adaptedEntry); 
     } 
     return adaptedMap; 
    } 

    @Override 
    public Map<String, LabelledElement> unmarshal(AdaptedMap adaptedMap) throws Exception { 
     List<AdaptedEntry> adaptedEntries = adaptedMap.entries; 
     Map<String, LabelledElement> map = new HashMap<String, LabelledElement>(); 
     for(AdaptedEntry adaptedEntry : adaptedEntries) { 
      map.put(adaptedEntry.key, adaptedEntry.value); 
     } 
     return map; 
    } 

} 

作爲參考,我的解決方案受到this link的啓發。

+0

感謝您的回答,但是,這並沒有幫助。即使我不關心「otherElements」,但我需要讀取標籤值,我正在將XML編組到文件中。 – user640853

+0

@ user640853對不起,它沒有幫助,你能解釋一下你在編組時的期望結果嗎? –

+0

我需要馬歇爾XML作爲是,這意味着所有元件和下'labelledElement'保持屬性,但我需要讀標籤的值 '

' 隨着你的解決方案,我會失去'otherElements'元素 – user640853

0

顯然,EclipseLink JAXB(MOXy)實現可能允許註釋接口變量,該接口變量提供將XML綁定到Java時使用的Factory類和方法,請參見answer

(編輯,以提供這種方法的一個例子) 例如,你代替具有一個抽象類LabelledElement,具有接口LabelledElement

public interface LabelledElement { 
    String getLabel(); 
} 

,然後有類A和乙實現它這樣的:

import javax.xml.bind.annotation.XmlElement; 

public class A implements LabelledElement{ 

    private String label; 

    @Override 
    @XmlElement(name="label") 
    public String getLabel() { 
     return label; 
    } 
} 

實體類註釋是這樣的:

@XmlRootElement(name = "Entity") 
@XmlAccessorType(XmlAccessType.NONE) 
public class Entity { 
    @XmlElement(name = "Header") 
    private Header header; 

    @XmlRootElement 
    @XmlType(
    factoryClass=Factory.class, 
    factoryMethod="createLabelledElement") 
    private LabelledElement labelledElement; 

    // constructors, getters, setters... 
} 

那麼,作爲答案,我掛暗示,你需要一個工廠類,如:

import java.lang.reflect.*; 
import java.util.*; 

public class Factory { 

    public A createA() { 
     return createInstance(A.class); 
    } 

    public B createB() { 
     return createInstance(B.class);; 
    } 

    private <T> T createInstance(Class<T> anInterface) { 
     return (T) Proxy.newProxyInstance(anInterface.getClassLoader(), new Class[] {anInterface}, new InterfaceInvocationHandler()); 
    } 

    private static class InterfaceInvocationHandler implements InvocationHandler { 

     private Map<String, Object> values = new HashMap<String, Object>(); 

     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 
      String methodName = method.getName(); 
      if(methodName.startsWith("get")) { 
       return values.get(methodName.substring(3)); 
      } else { 
       values.put(methodName.substring(3), args[0]); 
       return null; 
      } 
     } 

    } 
} 
+0

呃,不是真的!答案顯示瞭如何使用JAXB註釋相同的元素名稱,但使用與我正在查找的答案相反的不同類型。 – user640853

+0

@ user640853查看我的編輯回覆,作爲如何使用接口,Factory類和MOXy JAXB實現對其進行修改的示例。 – Guillem