JAXB - 尝试在“xsd:any”标记内使用枚举

时间:2014-02-26 05:20:10

标签: java jaxb

我会马上说,我毫不怀疑我的问题在一个纳秒内,我的问题是自己造成的,但我已经从我能想到的每一个角度自己解决了这个问题而且我'我仍然完全难过 - 任何帮助都会非常感激!

我想要完成的不是火箭科学 - 我已经给出了一个我称之为“提供”的模式,表示这是由第三方提供给我的(即我无法更改/编辑它)。请注意,此架构包含一个xsd:any标记,用作放置用户定义数据的位置。

<xsd:schema xmlns="http://somecompany.com/schema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://somecompany.com/schema">

<xsd:element name="Parent">
    <xsd:complexType>
        <xsd:sequence>
            <xsd:element name="UserDefinedArea" type="UserDefinedAreaType"/>
        </xsd:sequence>
    </xsd:complexType>
</xsd:element>

<xsd:complexType name="UserDefinedAreaType">
    <xsd:sequence>
        <xsd:any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
    </xsd:sequence>
</xsd:complexType>

</xsd:schema>

使用XJC为此模式生成代码非常简单。 XJC为UserDefinedAreaType生成一个类,它为我提供了一个用户定义数据的列表。

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "UserDefinedAreaType", propOrder = {
"any"
})
public class UserDefinedAreaType {

    @XmlAnyElement(lax = true)
    protected List<Object> any;
}

我想在这个UserDefinedArea中添加的内容之一是枚举(以提供一些额外的验证/类型安全保证)。创建一个模式来表示这一点也很容易。

<xsd:schema xmlns="http://mycompany.com/schema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://mycompany.com/schema">

<xsd:simpleType name="MyEnum">
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="active"/>
        <xsd:enumeration value="inactive"/>
    </xsd:restriction>
</xsd:simpleType>

</xsd:schema>

XJC愉快地为这个模式创建了一个类。

@XmlType(name = "MyEnum", namespace = "http://mycompany.com/schema")
@XmlEnum
public enum MyEnum {

    @XmlEnumValue("active")
    ACTIVE("active"),
    @XmlEnumValue("inactive")
    INACTIVE("inactive");
    private final String value;
}

现在有一些简单的代码来测试这个。

private static final ObjectFactory of = new ObjectFactory();

private static final JAXBContext jc;

static {
    try {
        jc = JAXBContext.newInstance(Parent.class);
    } catch (JAXBException ex) {
       throw new ExceptionInInitializerError(ex);
    }
}

public static void main(String[] args) {
    Parent parent = new Parent();
    UserDefinedAreaType udat = new UserDefinedAreaType();
    parent.setUserDefinedArea(udat);

    MyEnum myEnum = MyEnum.ACTIVE;
    udat.getAny().add(myEnum);

    File file = new File("output.txt");
    try {
        Marshaller m = jc.createMarshaller();
        m.marshal(parent, file);
    } catch (JAXBException ex) {
        ex.printStackTrace();
    }
}

这是我希望得到的。

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Parent xmlns:ns2="http://somecompany.com/schema" xlmns:ns3="http://mycompany.com/schema">
<UserDefinedArea>
    <ns3:MyEnum>active</ns3:MyEnum>
</UserDefinedArea>
</ns2:Parent>

但这就是我实际得到的。

javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: class com.mycompany.schema.MyEnum nor any of its super class is known to this context.

显而易见的事情是将MyEnum.class添加到JAXBContext(即“jc = JAXBContext.newInstance(Parent.class,MyEnum.class);”),但所有这些都是我:

javax.xml.bind.MarshalException
 - with linked exception:
[com.sun.istack.SAXException2: unable to marshal type "com.mycompany.schema.MyEnum" as an element because it is missing an @XmlRootElement annotation]

下一个显而易见的事情是将MyEnum(以及我计划填充到此UserArea中的任何其他内容)变成@XmlRootElement,但实际上我没有成功实现这一点!我要么缺少一种明显的方法来创建MyEnum作为@XmlRootElement,或者我的方法存在一些根本性的缺陷(即我从错误的方向接近这个问题)。

非常感谢任何人可能提出的任何想法。

2 个答案:

答案 0 :(得分:0)

解决方案一 -

架构变更:

<xsd:schema xmlns="http://mycompany.com/schema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://mycompany.com/schema">
    <xsd:element name="MyEnum" type="MyEnumType"/>
    <xsd:simpleType name="MyEnumType">
        <xsd:restriction base="xsd:string">
             <xsd:enumeration value="active"/>
             <xsd:enumeration value="inactive"/>
        </xsd:restriction>
    </xsd:simpleType>
</xsd:schema>

为“MyEnumType”创建一个类:

@XmlType(name = "MyEnumType", namespace = "http://mycompany.com/schema")
@XmlEnum
public enum MyEnumType {

    @XmlEnumValue("active")
    ACTIVE("active"),
    @XmlEnumValue("inactive")
    INACTIVE("inactive");
    private final String value;
}

还有一个有用的ObjectFactory方法,用于创建“MyEnums”(类型为“MyEnumType”):

@XmlElementDecl(namespace = "http://mycompany.com/schema", name = "MyEnum")
public JAXBElement<MyEnumType> createMyEnum(MyEnumType value) {
    return new JAXBElement<MyEnumType>(_MyEnum_QNAME, MyEnumType.class, null, value);
}

完整的试驾代码:

private static final ObjectFactory of = new ObjectFactory();

private static final JAXBContext jc;

static {
    try {
        jc = JAXBContext.newInstance(Parent.class, MyEnumType.class);
    } catch (JAXBException ex) {
        throw new ExceptionInInitializerError(ex);
    }
}

public static void main(String[] args) {

    Parent parent = new Parent();
    UserDefinedAreaType udat = new UserDefinedAreaType();
    parent.setUserDefinedArea(udat);

    JAXBElement<MyEnumType> goodEnum = of.createMyEnum(MyEnumType.fromValue("active"));
    udat.getAny().add(goodEnum);

    File file = new File("output.txt");

    try {
        Marshaller m = jc.createMarshaller();
        m.marshal(parent, file);
    } catch (JAXBException ex) {
        ex.printStackTrace();
    }
}

产生所需的输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:Parent xmlns:ns2="http://somecompany.com/schema">
    <UserDefinedArea>
        <ns3:MyEnum xmlns:ns3="http://mycompany.com/schema">active</ns3:MyEnum>
    </UserDefinedArea>
</ns2:Parent>

答案 1 :(得分:0)

xsd:any是任何元素的容器,但您的架构尚未声明任何元素,只有MyEnum 类型。如果您将附加架构编写为delcare MyEnum作为具有匿名类型而不是顶级类型的顶级元素

<xsd:schema xmlns="http://mycompany.com/schema" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://mycompany.com/schema">

<xsd:element name="MyEnum">
  <xsd:simpleType>
    <xsd:restriction base="xsd:string">
      <xsd:enumeration value="active"/>
      <xsd:enumeration value="inactive"/>
    </xsd:restriction>
  </xsd:simpleType>
</xsd:simpleType>

</xsd:schema>

然后你应该得到你需要的@XmlRootElement注释。