我正在尝试使用JAXB将通用超类ParentListElement的一个子类ChildListElement的多次出现变成列表。问题在于,JAXB取消了对超类而不是子类的编组。如何在不锁定一个子类ChildListElement的情况下让JAXB解组到ChildListElement而不是ParentListElement?
感谢您的时间和协助。 :-)
这些是文件/类:
main.java
ParentChildTests.java
ParentChildFactory.java
childBucket.xml
ParentBucket.java
ChildBucket.java
ParentListElement.java
ChildListElement.java
只有在以下情况下,才能在ParentBucket中使用包装器和元素注释:
指定了type=ChildListElement.class
。但这违反了具有泛型超类的目的,因为一个子类ChildListElement被锁定。我希望能够拥有ParentListElement的多个子类。每个列表一次仅包含一种子类。
Works-解组到ChildListElement,但将一个子类锁定:
@XmlElementWrapper( name = "elements" )
@XmlElements( { @XmlElement( name="element", type=ChildListElement.class ) } )
不起作用-取消编组到ParentListElement:
@XmlElementWrapper( name = "elements" )
@XmlElements( { @XmlElement( name="element" ) } )
main.java:
import GenListVsJaxbTests.ParentChildTests;
import javax.xml.bind.JAXBException;
public class Main
{
public static void main(String[] args) throws JAXBException
{
ParentChildTests.testChildBucket();
ParentChildTests.testChildBucketFromXml();
}
}
ParentChildTests.java:
package GenListVsJaxbTests;
import javax.xml.bind.JAXBException;
public class ParentChildTests
{
public static void testChildBucket()
{
ChildBucket bucket = ParentChildFactory.getNewChildBucket();
bucket.test();
}
public static void testChildBucketFromXml() throws JAXBException
{
ChildBucket bucket = ParentChildFactory.loadNewChildBucketFromXml ( "src/GenListVsJaxbTests/ChildBucket.xml" );
bucket.test();
}
}
ParentChildFactory.java:
package GenListVsJaxbTests;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ParentChildFactory
{
private static int MAX_ELEMENTS = 3;
public static ChildBucket getNewChildBucket()
{
ChildBucket childBucket = new ChildBucket();
List<ChildListElement> list = new ArrayList<>();
for ( int i = 0; i < MAX_ELEMENTS; i ++ )
{
ChildListElement el = new ChildListElement();
el.setParentListElMember ( String.valueOf ( i ) );
el.setChildListElMember ( String.valueOf ( i + 10 ) );
list.add ( el );
}
childBucket.setElementList ( list );
return childBucket;
}
public static ChildBucket loadNewChildBucketFromXml ( String fileName ) throws JAXBException
{
File inFile = new File( fileName );
JAXBContext jaxbContext = JAXBContext.newInstance ( ChildBucket.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
ChildBucket bucket = (ChildBucket) jaxbUnmarshaller.unmarshal(inFile);
return bucket;
}
}
childBucket.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<childBucket
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation='ChildBucket.xsd'
>
<childBucketMember>child bucket</childBucketMember>
<parentBucketMember>parent bucket</parentBucketMember>
<elements>
<element>
<childListElMember>child element 1</childListElMember>
<parentListElMember>parent element 1</parentListElMember>
</element>
<element>
<childListElMember>child element 2</childListElMember>
<parentListElMember>parent element 2</parentListElMember>
</element>
<element>
<childListElMember>child element 3</childListElMember>
<parentListElMember>parent element 3</parentListElMember>
</element>
<element>
<childListElMember>child element 4</childListElMember>
<parentListElMember>parent element 4</parentListElMember>
</element>
</elements>
</childBucket>
ParentBucket.java:
package GenListVsJaxbTests;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import java.util.List;
public class ParentBucket<LE extends ParentListElement>
{
protected String parentBucketMember = "parentBucketMember";
List<LE> elementList;
public String getParentBucketMember()
{
return parentBucketMember;
}
public void setParentBucketMember(String parentBucketMember)
{
this.parentBucketMember = parentBucketMember;
}
public List<LE> getElementList()
{
return elementList;
}
@XmlElementWrapper( name = "elements" )
@XmlElements( { @XmlElement( name="element" ) } )
// @XmlElements( { @XmlElement( name="element", type=ChildListElement.class ) } )
public void setElementList(List<LE> elementList)
{
this.elementList = elementList;
}
public void test()
{
System.out.println("ParentBucket.test");
System.out.println("parentBucketMember: " + parentBucketMember);
for ( LE el : elementList )
{
el.test();
}
}
}
ChildBucket.java:
package GenListVsJaxbTests;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class ChildBucket extends ParentBucket<ChildListElement>
{
protected String childBucketMember = "childBucketMember";
public String getChildBucketMember()
{
return childBucketMember;
}
public void setChildBucketMember(String childBucketMember)
{
this.childBucketMember = childBucketMember;
}
public void test()
{
System.out.println("ChildBucket.test");
System.out.println("childBucketMember: " + childBucketMember);
super.test();
System.out.println("---");
for ( ChildListElement el : elementList )
{
el.test();
}
System.out.println("===");
}
}
ParentListElement.java:
package GenListVsJaxbTests;
public class ParentListElement
{
protected String parentListElMember = "parentListElMember";
public String getParentListElMember()
{
return parentListElMember;
}
public void setParentListElMember(String parentListElMember)
{
this.parentListElMember = parentListElMember;
}
public void test()
{
System.out.println("ParentListElement.test");
System.out.println("parentListElMember: " + parentListElMember);
}
}
ChildListElement.java:
package GenListVsJaxbTests;
public class ChildListElement extends ParentListElement
{
protected String childListElMember = "childListElMember";
public String getChildListElMember()
{
return childListElMember;
}
public void setChildListElMember(String childListElMember)
{
this.childListElMember = childListElMember;
}
@Override
public void test()
{
super.test();
System.out.println("ChildListElement.test");
System.out.println("childListElMember: " + childListElMember);
}
}
随后的for循环在运行时抛出此错误(缩写):
ClassCastException: ParentListElement cannot be cast to ChildListElement
正确编组时,输出如下所示:
"C:\Program Files\Java\jdk1.8.0_211\bin\java" -Didea.launcher.port=7538 "-Didea.launcher.bin.path=[SNIP] com.intellij.rt.execution.application.AppMain com.caci.irma.experiment.Main
ChildBucket.test
childBucketMember: childBucketMember
ParentBucket.test
parentBucketMember: parentBucketMember
ParentListElement.test
parentListElMember: 0
ChildListElement.test
childListElMember: 10
ParentListElement.test
parentListElMember: 1
ChildListElement.test
childListElMember: 11
ParentListElement.test
parentListElMember: 2
ChildListElement.test
childListElMember: 12
---
ParentListElement.test
parentListElMember: 0
ChildListElement.test
childListElMember: 10
ParentListElement.test
parentListElMember: 1
ChildListElement.test
childListElMember: 11
ParentListElement.test
parentListElMember: 2
ChildListElement.test
childListElMember: 12
===
ChildBucket.test
childBucketMember: child bucket
ParentBucket.test
parentBucketMember: parent bucket
ParentListElement.test
parentListElMember: parent element 1
ChildListElement.test
childListElMember: child element 1
ParentListElement.test
parentListElMember: parent element 2
ChildListElement.test
childListElMember: child element 2
ParentListElement.test
parentListElMember: parent element 3
ChildListElement.test
childListElMember: child element 3
ParentListElement.test
parentListElMember: parent element 4
ChildListElement.test
childListElMember: child element 4
---
ParentListElement.test
parentListElMember: parent element 1
ChildListElement.test
childListElMember: child element 1
ParentListElement.test
parentListElMember: parent element 2
ChildListElement.test
childListElMember: child element 2
ParentListElement.test
parentListElMember: parent element 3
ChildListElement.test
childListElMember: child element 3
ParentListElement.test
parentListElMember: parent element 4
ChildListElement.test
childListElMember: child element 4
===
Process finished with exit code 0
错误编组时,输出如下所示:
"C:\Program Files\Java\jdk1.8.0_211\bin\java" -Didea.launcher.port=7542 "-Didea.launcher.bin.path=[SNIP] com.intellij.rt.execution.application.AppMain com.caci.irma.experiment.Main
ChildBucket.test
childBucketMember: childBucketMember
ParentBucket.test
parentBucketMember: parentBucketMember
ParentListElement.test
parentListElMember: 0
ChildListElement.test
childListElMember: 10
ParentListElement.test
parentListElMember: 1
ChildListElement.test
childListElMember: 11
ParentListElement.test
parentListElMember: 2
ChildListElement.test
childListElMember: 12
---
ParentListElement.test
parentListElMember: 0
ChildListElement.test
childListElMember: 10
ParentListElement.test
parentListElMember: 1
ChildListElement.test
childListElMember: 11
ParentListElement.test
parentListElMember: 2
ChildListElement.test
childListElMember: 12
===
ChildBucket.test
childBucketMember: child bucket
ParentBucket.test
parentBucketMember: parent bucket
ParentListElement.test
parentListElMember: parent element 1
ParentListElement.test
parentListElMember: parent element 2
ParentListElement.test
parentListElMember: parent element 3
ParentListElement.test
parentListElMember: parent element 4
---
Exception in thread "main" java.lang.ClassCastException: GenListVsJaxbTests.ParentListElement cannot be cast to GenListVsJaxbTests.ChildListElement
at GenListVsJaxbTests.ChildBucket.test(ChildBucket.java:27)
at GenListVsJaxbTests.ParentChildTests.testChildBucketFromXml(ParentChildTests.java:17)
at com.caci.irma.experiment.Main.main(Main.java:14)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Process finished with exit code 1
答案 0 :(得分:0)
以下问题与我的类似:
Marshalling/Unmarshalling Java superclass and subclasses using JAXB
使用@XmlElements批注,可以指定多个@ XmlElement / class条目。这意味着您可以为不同的@XmlElement名称使用不同的目标类。
@XmlElementWrapper( name = "elements" )
@XmlElements
( {
@XmlElement( name="el1", type=ChildListElement1.class ),
@XmlElement( name="el2", type=ChildListElement2.class )
} )
但是,这仍然意味着超类必须了解子类。每次创建子类时,也都需要修改超类,以使该子类成为JAXB目标类。似乎有点紧密耦合。同样,每个类都需要有自己唯一的元素名称。这使得每个XML文件都需要其自己的架构,或者,如果架构更复杂并且允许同一结构使用不同的元素名称,那么它们之间可以共享一个架构。
我阅读了有关IDResolver.bind,jaxb:bindings,SchemaFactory,AnnotationHelper,StreamReaderDelegate等的各种文章。jaxb:bindings看起来很有前途,似乎它可以允许为给定节点名称指定目标类。但是,在解组期间实际上会忽略XSD架构中的绑定。
另一种可能解决此问题的方法是:
超类:
public abstract class ParentBucket<LE extends ParentListElement>
{
List<LE> elementList;
public abstract void setElementList(List<LE> elementList);
public abstract List<LE> getElementList();
}
子类1:
@XmlRootElement
public class ChildBucket1 extends ParentBucket<ChildListElement1>
{
@Override
@XmlElementWrapper( name = "elements" )
@XmlElements ( { @XmlElement( name="element" ) } )
// @XmlElements ( { @XmlElement( name="element", type=ChildListElement1.class ) } )
public void setElementList(List<ChildListElement1> elementList)
{
super.setElementListCore ( elementList );
}
@Override
public List<ChildListElement1> getElementList()
{
return super.getElementListCore();
}
}
子类2:
@XmlRootElement
public class ChildBucket2 extends ParentBucket<ChildListElement2>
{
@Override
@XmlElementWrapper( name = "elements" )
@XmlElements ( { @XmlElement( name="element" ) } )
// @XmlElements ( { @XmlElement( name="element", type=ChildListElement2.class ) } )
public void setElementList(List<ChildListElement2> elementList)
{
super.setElementListCore ( elementList );
}
@Override
public List<ChildListElement2> getElementList()
{
return super.getElementListCore();
}
}
最初,它在我的实验代码中起作用,但在“真实”代码中却不起作用。花了一段时间才弄清楚为什么它在一个项目中起作用,而不在另一个项目中起作用。 几乎放弃给超类添加注释并继续前进。
但是:-)我发现了问题所在。注释子类,如果Java成员列表名称为“ fields”,并且将getter / setter命名为getFields / setFields,则JAXB无法“查找”子类并尝试实例化超类。 JAXB不会警告任何冲突。尝试实例化抽象类时,运行时将失败。或者,如果超类不是抽象的,则以后带有具体子类的“ for”循环将因ClassCastException而失败。通过将列表变量的名称从“ fields”更改为“ listOfFields”,以及相应的抽象和已实现的getter / setter,就可以了!
问题超类:
public abstract class ParentBucketOops<FE extends FieldElement>
{
List<FE> fields; // oops, JAXB has a runtime problem later
public abstract void setFields(List<FE> fields);
public abstract List<FE> getFields();
}