对不起,很长的文章,谢谢! 解组嵌套使用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不知道那些泛型是什么?我敢打赌,这是我的情况,但我不知道如何检查或修复该问题。