JAXB 注释 - 如何使 XmlIDRef 元素列表具有 id 值作为属性而不是元素正文文本



更新 - 请参阅底部的编辑

IDRefs/keyrefs 在 JAXB 注解中似乎是可能的,但 ref 最终变成了元素文本。

我希望 ref 是一个元素的属性。

例如,给定此对象模型:

@XmlType
public class Employee {
    @XmlID
    @XmlAttribute
    String name;
    @XmlAttribute
    int years;
    @XmlAttribute
    String foo;
}
@XmlType
public class Office {
    @XmlAttribute
    String name;
    @XmlElementWrapper
    @XmlElement(name = "employee")
    List<Employee> employees;
}
@XmlRootElement
public class Company {
    @XmlElementWrapper
    @XmlElement(name = "office")
    List<Office> offices;
    @XmlElementWrapper
    @XmlElement(name = "employee")
    List<Employee> employees;
}

我希望外部化的xml格式最终看起来像这样:

<company>
    <offices>
        <office name="nyc">
            <employees>
                <!--*** id ref to employee name ***-->
                <employee ref="alice"/>
                <employee ref="bob"/>
            </employees>
        </office>
        <office name="sf">
            <employees>
                <employee ref="connie"/>
                <employee ref="daphne"/>
            </employees>
        </office>
    </offices>
    <employees>
        <!-- *** name is the id *** -->
        <employee name="alice" years="3" foo="bar"/>
        <employee name="bob" years="3" foo="bar"/>
        <employee name="connie" years="3" foo="bar"/>
        <employee name="daphne" years="3" foo="bar"/>
    </employees>
</company>

相反,我能做的最好的事情就是这样做(使用上面在java代码中列出的注释):

<company>
    <offices>
        <office name="nyc">
            <employees>
                <employee>alice</employee>
                <employee>bob</employee>
            </employees>
        </office>
        <office name="sf">
            <employees>
                <employee>connie</employee>
                <employee>daphne</employee>
            </employees>
        </office>
    </offices>
    <employees>
        <employee name="alice" years="3" foo="bar"/>
        <employee name="bob" years="3" foo="bar"/>
        <employee name="connie" years="3" foo="bar"/>
        <employee name="daphne" years="3" foo="bar"/>
    </employees>
</company>

有没有办法强制 idref 值是员工的属性,而不是元素正文文本? 我知道我可以使用 XML 架构来做到这一点,但如果可能的话,我想坚持使用注释。

谢谢。

编辑下面的 Torious 的解决方案几乎有效,但在某些情况下不太有效。

如果"Office"元素位于 Office 引用的"员工"元素之前(在 XML 文件中),则解组失败。 找不到员工引用,并且 EmployeeRef 包装器具有空员工对象。 如果"员工"是第一位的,它就有效。

这不会是一个大问题,但封送方法会将"办公室"放在首位,因此尝试解组刚刚编组的内容会失败。

在Torious的答案中编辑2条评论解决了排序问题。

解决方案是使用将Employee包装在新类型的实例中的XmlAdapterEmployeeRef ,它指定如何映射 XML id ref:

@XmlType
public class Office {
    @XmlAttribute
    String name;
    @XmlElementWrapper
    @XmlElement(name="employee")
    @XmlJavaTypeAdapter(EmployeeAdapter.class) // (un)wraps Employee
    List<Employee> employees;
}
@XmlType
public class EmployeeRef {
    @XmlIDREF
    @XmlAttribute(name="ref")
    Employee employee;
    public EmployeeRef() {
    }
    public EmployeeRef(Employee employee) {
        this.employee = employee;
    }
}
public class EmployeeAdapter extends XmlAdapter<EmployeeRef, Employee> {
    @Override
    public EmployeeRef marshal(Employee employee) throws Exception {
        return new EmployeeRef(employee);
    }
    @Override
    public Employee unmarshal(EmployeeRef ref) throws Exception {
        return ref.employee;
    }
}

祝你好运。

注意:我是 EclipseLink JAXB (MOXy) 的负责人,也是 JAXB (JSR-222) 专家组的成员。

下面是一个示例,说明如何通过利用@XmlPath注释来使用 MOXy 完成此操作。 由于存在一个错误,您需要从 2012 年 5 月 17 日开始使用 EclipseLink 2.4.0 夜间标签。 此修复程序也已添加到 EclipseLink 2.3.3(从 2012 年 5 月 18 日开始)流中。 您可以从以下位置下载夜间标签:

  • http://www.eclipse.org/eclipselink/downloads/nightly.php

办公室

employees 属性上,可以将@XmlIDREF批注与@XmlPath批注结合使用,以获取所需的映射。 @XmlIDREF告诉 JAXB 实现写出外键而不是对象。

package forum10150263;
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@XmlType
public class Office {
    @XmlAttribute
    String name;
    @XmlPath("employees/employee/@ref")
    @XmlIDREF
    List<Employee> employees;
}

员工

@XmlIDREF对应的是 @XmlID . @XmlID用于指定对象的主键。

package forum10150263;
import javax.xml.bind.annotation.*;
@XmlType
public class Employee {
    @XmlID
    @XmlAttribute
    String name;
    @XmlAttribute
    int years;
    @XmlAttribute
    String foo;
}

公司

通过@XmlIDREF机制引用的每个对象也需要通过包含关系引用。 在此示例中,这是由 employees 属性完成的。

package forum10150263;
import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Company {
    @XmlElementWrapper
    @XmlElement(name = "office")
    List<Office> offices;
    @XmlElementWrapper
    @XmlElement(name = "employee")
    List<Employee> employees;
}

JAXB.properties

要将 MOXy 指定为 JAXB 提供程序,

您需要在域模型所在的同一包中添加一个名为 jaxb.properties 的文件,并使用以下条目(请参阅将 EclipseLink MOXy 指定为 JAXB 提供程序)

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

演示

标准 JAXB API 用于解组/编组 XML。

package forum10150263;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Company.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum10150263/input.xml");
        Company company = (Company) unmarshaller.unmarshal(xml);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(company, System.out);
    }
}

输入.xml/输出

<?xml version="1.0" encoding="UTF-8"?>
<company>
   <offices>
      <office name="nyc">
         <employees>
            <employee ref="alice"/>
            <employee ref="bob"/>
         </employees>
      </office>
      <office name="sf">
         <employees>
            <employee ref="connie"/>
            <employee ref="daphne"/>
         </employees>
      </office>
   </offices>
   <employees>
      <employee name="alice" years="3" foo="bar"/>
      <employee name="bob" years="3" foo="bar"/>
      <employee name="connie" years="3" foo="bar"/>
      <employee name="daphne" years="3" foo="bar"/>
   </employees>
</company>

欲了解更多信息

  • 指定 EclipseLink MOXy 作为您的 JAXB 提供程序
  • JAXB 和共享参考文献:@XmlID和@XmlIDREF
  • 基于 XPath 的映射

最新更新