通用的JAXB适配器

4
假设我有一个名为Person的类:
class Person{
  String firstName;
  String lastName;
  String email;
}

XML的格式如下:

<person>
 <firstName value="asd" /> 
 <lastName value="bcd" />
 <email value="qwe" />
</person>
我可以使用自己的XmlAdapter实现来解组/编组这个类的每个字段FirstNameAdapter、LastNameAdapter、EmailAdapter。如您所见,每个字段都以相似的方式表示——字段名称作为xml元素,字段值作为元素的属性。是否有可能创建一个“通用”适配器,我将能够传输该字段的名称,并从中提取该字段的值? P.S. 我知道MOXy JAXB实现,但我想知道是否可以通过参考JAXB实现来实现。 谢谢!
2个回答

5
你可以像这样使用XmlAdapter
import java.io.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.*;

@XmlType
class Valued {
    @XmlAttribute(name="value")
    public String value;
}

class ValuedAdapter extends XmlAdapter<Valued, String> {
    public Valued marshal(String s) {
        Valued v = new Valued();
        v.value = s;
        return v;
    }
    public String unmarshal(Valued v) {
        return v.value;
    }
}

@XmlRootElement
class Person {

    @XmlJavaTypeAdapter(ValuedAdapter.class)
    @XmlElement
    String firstName;

    @XmlJavaTypeAdapter(ValuedAdapter.class)
    @XmlElement
    String lastName;

}

class SO12928971 {
    public static void main(String[] args) throws Exception {
        Person p = new Person();
        p.firstName = "John";
        p.lastName = "Doe";
        JAXBContext jc = JAXBContext.newInstance(Person.class);
        StringWriter sw = new StringWriter();
        jc.createMarshaller().marshal(p, sw);
        String xml = sw.toString();
        System.out.println(xml);
        StringReader sr = new StringReader(xml);
        p = (Person)jc.createUnmarshaller().unmarshal(sr);
        assert "John".equals(p.firstName);
        assert "Doe".equals(p.lastName);
    }
}
这里的想法是,XML架构以及JAXB在“元素名称”和“内容类型”之间有明显区分,尽管许多文档在这两者之间有明确的一对一对应关系。因此,在上面的代码中,“值为Valued”的类型描述了具有“value”属性的内容,而不考虑元素名称。要序列化的成员用@XmlElement注释,并未在该注释中包含名称。因此,它们将生成从成员名称派生出来的名称的元素。@XmlJavaTypeAdapter注释会导致序列化程序将这些成员视为其类型为“Valued”。因此,这就是它们的XML内容类型。 上述代码的架构如下:
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">

  <xs:element name="person" type="person"/>

  <xs:complexType name="person">
    <xs:sequence>
      <xs:element name="firstName" type="valued" minOccurs="0"/>
      <xs:element name="lastName" type="valued" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="valued">
    <xs:sequence/>
    <xs:attribute name="value" type="xs:string"/>
  </xs:complexType>
</xs:schema>

1
+1 表示 "JAXB 在元素名称和内容类型之间有明确的区分"。 - Ian Roberts

-1

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

P.S. 我知道MOXy JAXB实现,但我想知道是否可以通过参考JAXB实现来实现。

为了比较起见,我已经添加了如何使用EclipseLink JAXB(MOXy)的@XmlPath扩展来完成此操作。

人员

package forum12928971;

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Person{

    @XmlPath("firstName/@value")
    String firstName;

    @XmlPath("lastName/@value")
    String lastName;

    @XmlPath("email/@value")
    String email;

}

jaxb.properties

如果要将MOXy指定为您的JAXB提供程序,则需要在与您的域模型相同的包中添加一个名为jaxb.properties的文件,并添加以下条目:

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

演示

package forum12928971;

import java.io.File;
import javax.xml.bind.*;

public class Demo {
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Person.class);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum12928971/input.xml");
        Person person = (Person) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(person, System.out);
    }

}

input.xml/Output

<?xml version="1.0" encoding="UTF-8"?>
<person>
   <firstName value="asd"/>
   <lastName value="bcd"/>
   <email value="qwe"/>
</person>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,