對不起,很長的文章,謝謝! 解組嵌套使用JAXB設置
我需要來解讀這個XML:
<RootTag>
<Container type="type1">
<value name="name1"/>
<value name="name2"/>
</Container>
<Container type="type2">
<value name="name3"/>
<value name="name4"/>
</Container>
</RootTag>
摘要
在我的JAXB(代碼如下)我有值,其中內部的一組被包裹的集的集成集裝箱類。所以,我有一組值的容器,其中一個值是一個泛型類。問題:除非所選的泛型類是硬編碼的,否則值不會被解析。
詳細
注意的XML:
<Container>
標籤包含的<value>
標籤一個設置。<value>
標籤可能包含任何內容。在JAXB中,集裝箱類使用集<Ť>(在上面的例子中,我們有一個的ElementName類,僅具有一個「name」屬性)。有隻應該是正好兩個
<Container>
標籤(它的屬性是兩個項目的枚舉,但在這個例子中,這只是一個字符串,使其更簡單)。 附加問題(可選):是否有任何方法將標籤的數量限制爲兩個(不多也不少)?
結束語所有這一切,我們有一個根類,它具有Set<Container<ElementName>>
。問題在於,最深的ElementName
(應該表示<value>
標記)類沒有被JAXB解決(太多的間接級別?)。然而,JAXB將所有信息讀入其內部類型ElementNSImpl
(我通過在afterUnmarshall
方法中放置一個斷點來計算出它),它具有標籤的屬性值(即,name1
,name2
,,name4
)。解組完成時沒有錯誤,但是當我嘗試訪問任何<value>
標籤我得到這個:
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.ElementNSImpl cannot be cast to ElementName
裏面的集裝箱類,我用
@XmlElement(name = "value")
如果我指定的<value>
的標籤名只是硬編碼類型如下:
@XmlElement(name = "value", type = ElementName.class)
然後JAXB正確解析標籤。但是,正如我所說的,這個班級應該是通用的,所以這對我來說不是一個真正的解決方案。
JAXB類
根
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/*
* <RootTag>
* *
* </RootTag>
*/
@XmlRootElement(name = "RootTag")
public class Root {
/* <Container type = "type1">
* <value name = "name1"/>
* <value name = "name2"/>
* </Container>
* <Container type = "type2">
* <value name = "name3"/>
* <value name = "name4"/>
* </Container>
*/
@XmlElement(name = "Container")
private Set<Container<ElementName>> containers;
// for each of the two Container types
// we create a set of its corresponding values' names
// and then we null the containers field
@XmlTransient
private Map<String, Set<String>> valuesNames = new HashMap<>(2);
//Basically that is @PostConstruct, but works with JAXB
void afterUnmarshal(Unmarshaller u, Object parent) {
containers.forEach(container -> valuesNames.put(container.getType(),
container.getValues().stream()
.map(ElementName::getName)
.collect(Collectors.toSet())));
containers = null;
}
/** return unique value names from both container types */
public Set<String> getAllValuesNames() {
return valuesNames.values().stream()
.flatMap(Set::stream)
.collect(Collectors.toSet());
}
}
集裝箱
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.*;
import java.util.Set;
/*
* <* type = "type1">
* <value *>*
* <value *>*
* </*>
*/
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Container<T> {
@XmlAttribute
private String type;
@XmlElement(name = "value")
private Set<T> values;
public String getType() {
return type;
}
public Set<T> getValues() {
return values;
}
}
的ElementName
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
/*
* <* name = "name2"/>
*/
@XmlAccessorType(XmlAccessType.FIELD)
public class ElementName {
@XmlAttribute
private String name;
public String getName() {
return name;
}
}
主要
public static void main(String[] args) throws JAXBException {
final String xmlSerialized = "<RootTag>\n" +
" \n" +
" <Container type=\"type1\">\n" +
" <value name=\"name1\"/>\n" +
" <value name=\"name2\"/>\n" +
" </Container>\n" +
" \n" +
" <Container type=\"type2\">\n" +
" <value name=\"name3\"/>\n" +
" <value name=\"name4\"/>\n" +
" </Container>\n" +
" \n" +
"</RootTag>";
System.out.println("XML:\n" +
"=======================================\n" +
xmlSerialized +
"\n=======================================");
//works just the same with only Root.class
JAXBContext context = JAXBContext.newInstance(Root.class, Container.class, ElementName.class);
Unmarshaller unmarshaller = context.createUnmarshaller();
Root xmlDeserialized = (Root) unmarshaller.unmarshal(new StringReader(xmlSerialized));
System.out.println("Values' names: " + xmlDeserialized.getAllMetricNames());
}
如果一切正常(如果我硬編碼ElementName.class在集裝箱類中的註釋)它提供了以下的輸出:
XML:
=======================================
<RootTag>
<Body>
<Container type="type1">
<value name="name1"/>
<value name="name2"/>
</Container>
<Container type="type2">
<value name="name3"/>
<value name="name4"/>
</Container>
</Body>
</RootTag>
=======================================
Values' names: [name4, name3, name2, name1]
我試過的東西
Unmarshalling generic list with JAXB。
主要思路是不是使用
List<T>
使用Wrapper<T>
,將使用該名單內,並通過getter給它。我的容器已經是一種包裝,除了它也有一個屬性。但在集的註解來區別:前:
@XmlElement(name = "value") private Set<T> values;
後:
@XmlAnyElement(lax = true) private Set<T> values;
再次運行(
JAXBContext.newInstance(Root.class, Container.class, ElementName.class)
):沒有什麼變化,價值仍是鍵入ElementNSImpl
。另外,我不喜歡我允許任何名稱的標籤,它必須是「價值」。
Intermittent ClassCastException from ElementNSImpl to own type during unmarshalling
在他們的情況下,問題是,他們創造JAXBContext
不提供一些必要的類。不適用於我。
JAXB Unmarshalling XML Class Cast Exception
在他們的情況下,問題是Java的類型擦除,這意味着Java的編譯時確定泛型類型,然後編譯代碼,然後將擦除泛型類型,所以JVM不知道那些泛型是什麼?我敢打賭,這是我的情況,但我不知道如何檢查或修復該問題。