我想根据属性值将 JAXB bean 解码为子类。
MOXy 可以做到这一点:
@XmlDiscriminatorNode("@type")
public static class ActionDef extends ContainerOfStackableDefs { ... }
@XmlDiscriminatorValue("cli")
public static class CliActionDef extends ActionDef {
/** CLI command. EL. */
@XmlAttribute public String command;
}
但是,JAXBContext
需要指定的所有类。在“根”类之外的其他地方进行定义有点令人不快。
我希望是这样的:
@XmlDiscriminatorNode("@type")
@XmlSubClasses({ CliActionDef.class, XsltActionDef.class, ... })
public static class ActionDef extends ContainerOfStackableDefs { ... }
我不想进入 XmlAdapter
,这是一个微不足道的任务,应该有微不足道的解决方案。
有什么东西,无论是规范还是 MOXy 扩展,我可以用来简单地列出根类中的子类?
否则,我将使用 Jandex 自动查找子类。毕竟,MOXy 也可以做到这一点 ;-)
更新:为了记录,然后我得到了
Descriptor: XMLDescriptor(org.jboss.loom.migrators._ext.MigratorDefinition$ActionDef --> [])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassIndicatorField(DescriptorException.java:957)
...
简而言之:不要将 MOXy 解码到与 @XmlDiscriminatorNode("@name")
中名称相同的属性。如果你这样做,那么拥有它 @XmlReadOnly
并且没有类抽象。
更新:我仍然无法让它工作。我一直在上基础课。由 MOXy 处理,而不是 JDK 的实现。
我的代码:
<action type="xslt" var="addAction" ...>
</action>
<action type="manual">
...
</action>
bean 类:
//@XmlRootElement
@XmlDiscriminatorNode("@type")
@XmlSeeAlso({ CliActionDef.class, ModuleActionDef.class, CopyActionDef.class, XsltActionDef.class })
public static class ActionDef extends ContainerOfStackableDefs {
//@XmlAttribute
//@XmlReadOnly
public String typeVal; // Was type, caused an exception.
//public List<PropertyBean> properties;
//@XmlAnyAttribute
public Map<String, String> attribs = new HashMap();
}
@XmlRootElement
@XmlDiscriminatorValue("manual")
public static class ManualActionDef extends ActionDef {
}
public static class FileBasedActionDef extends ActionDef {
/** Path mask. Ant-like wildcards, EL. */
@XmlAttribute public String pathMask;
/** Where to store the result. May be a dir or a file. EL. */
@XmlAttribute public String dest;
}
@XmlRootElement
@XmlDiscriminatorValue("xslt")
public static class XsltActionDef extends FileBasedActionDef {
/** XSLT template path. */
@XmlAttribute public String xslt;
}
怎么了?
更新:
将@XmlDiscriminatorNode("@type") 放入最顶层的类 ContainerOfStackableDefs
后,我得到:
Exception Description: Missing class for indicator field value [manual] of type [class java.lang.String].
Descriptor: XMLDescriptor(org.jboss.loom.migrators._ext.MigratorDefinition$ActionDef --> [])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassForIndicatorFieldValue(DescriptorException.java:938)
at org.eclipse.persistence.internal.oxm.QNameInheritancePolicy.classFromRow(QNameInheritancePolicy.java:264)
简而言之:缺少 java.lang.String
。 :-o 可解决?
此外,我创建了一个最简单的测试用例:
public class JaxbInheritanceTest {
@Test
public void testUnmarshall() throws JAXBException{
final Unmarshaller marshaller = XmlUtils.createJaxbContext(Root.class).createUnmarshaller();
Root root = (Root) marshaller.unmarshal( new StringReader(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<root><sub disc=\"foo\"/><sub disc=\"bar\"/></root>") );
boolean rightClass = (DiscFoo.class.isAssignableFrom( root.subs.get(0).getClass() ));
Assert.assertTrue( "base elements go into subclasses", rightClass );
rightClass = (DiscBar.class.isAssignableFrom( root.subs.get(1).getClass() ));
Assert.assertTrue( "base elements go into subclasses", rightClass );
}
@XmlRootElement
public static class Root {
@XmlElement(name = "sub")
List<Base> subs;
}
@XmlDiscriminatorNode("@disc")
@XmlSeeAlso({DiscFoo.class, DiscBar.class})
public static abstract class Base {}
@XmlRootElement @XmlDiscriminatorValue("foo")
public static class DiscFoo {}
@XmlRootElement @XmlDiscriminatorValue("bar")
public static class DiscBar {}
}
但这也给了我 2x Base
。它由 MOXy 处理 - 在调试器中检查。
这里有什么问题吗?
最佳答案
您可以为此使用 @XmlSeeAlso
(javax.xml.bind.annotation) 注释。
@XmlDiscriminatorNode("@type")
@XmlSeeAlso({ CliActionDef.class, XsltActionDef.class, ... })
public static class ActionDef extends ContainerOfStackableDefs { ... }
更新
您在最新更新中看到的问题是由于 @XmlDescriminatorNode
未放置在继承层次结构中的根类中引起的。当我使用 @XmlTransient
从继承层次结构中删除 ContainerOfStackableDefs
时,一切正常(参见:http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html)。
演示
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.*;
public class Demo {
@XmlTransient
public static class ContainerOfStackableDefs {
}
@XmlDiscriminatorNode("@type")
@XmlSeeAlso({ ManualActionDef.class, FileBasedActionDef.class, XsltActionDef.class })
public static class ActionDef extends ContainerOfStackableDefs {
//@XmlAttribute
//@XmlReadOnly
public String typeVal; // Was type, caused an exception.
//public List<PropertyBean> properties;
//@XmlAnyAttribute
public Map<String, String> attribs = new HashMap();
}
@XmlDiscriminatorValue("manual")
public static class ManualActionDef extends ActionDef {
}
@XmlDiscriminatorValue("fileBased")
public static class FileBasedActionDef extends ActionDef {
/** Path mask. Ant-like wildcards, EL. */
@XmlAttribute public String pathMask;
/** Where to store the result. May be a dir or a file. EL. */
@XmlAttribute public String dest;
}
@XmlDiscriminatorValue("xslt")
public static class XsltActionDef extends FileBasedActionDef {
/** XSLT template path. */
@XmlAttribute public String xslt;
}
@XmlRootElement
public static class Root {
@XmlElement(name="actionDef")
public List<ActionDef> actionDefs;
}
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17453793/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<actionDef type="manual"/>
<actionDef pathMask="PATH MASK" dest="DEST" type="fileBased"/>
<actionDef xslt="XSLT" type="xslt"/>
</root>
关于inheritance - JAXB 继承 - 解码为注释中声明的子类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17453793/