【问题标题】:JAXB inheritance - unmarshalling to subtypes declared in annotations?JAXB 继承 - 解组到注释中声明的子类型?
【发布时间】:2013-07-03 16:51:08
【问题描述】:

我想根据属性值将 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 { ... }

我不想进入XmlAdapters,这是一项微不足道的任务,应该有微不足道的解决方案。

有什么东西,无论是规范还是 MOXy 扩展,我都可以用来简单地列出根类中的子类吗?

否则,我将使用 Jandex 自动查找子类。毕竟,MOXy 也可以做到这一点;-)


更新:为了记录,然后我得到了

Descriptor: XMLDescriptor(org.jboss.loom.migrators._ext.MigratorDefinition$ActionDef --> [])
at org.eclipse.persistence.exceptions.DescriptorException.missingClassIndicatorField(DescriptorException.java:957)
    ...

这里有描述:Exception : "Missing class indicator field from database row [UnmarshalRecordImpl()]." when unmarshalling XML using EclipseLink JAXB (MOXy)

简而言之:不要将 MOXy 解组到与 @XmlDiscriminatorNode("@name") 中的名称相同的属性。如果你这样做了,那么就拥有它@XmlReadOnly 并且没有类抽象。


更新:我仍然无法让它工作。我不断得到基类。由 MOXy 处理,而不是 JDK 的 impl。

我的代码:

    <action type="xslt" var="addAction" ...>
    </action>

    <action type="manual">
        ...
    </action>

豆子:

//@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 处理 - 在调试器中检查。
这里有什么问题?

【问题讨论】:

    标签: inheritance jaxb moxy subclass


    【解决方案1】:

    您可以为此使用@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>
    

    【讨论】:

    • @OndraŽižka - 没问题。 @XmlSeeAlso 在 JAXB 2.1 中被引入来解决这个问题。
    • 顺便说一句,对于我事先并不真正了解子类的情况,您会考虑添加 Jandex 解决方案吗?例如。插件等
    • @OndraŽižka - 您介意为此输入增强请求吗?:bugs.eclipse.org/bugs/enter_bug.cgi?product=EclipseLink
    • 并且(请原谅在这个问题上滥用 SO)- 是否考虑/正在进行一些 CDI 和 MOXy 集成?让 CDI 解决各种工厂方法、适配器等可能会很酷。
    • 关于继承根 - 我认为在任意级别声明 JAXB 的层次结构根是有意义的。我需要ContainerOfStackableDefs,因为它捕获了从 JAXB 加载的其他结构的共同特征。这符合增强请求的条件吗?
    猜你喜欢
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多