Reputation: 648
Let's say I have class Person:
class Person{
String firstName;
String lastName;
String email;
}
XML has format:
<person>
<firstName value="asd" />
<lastName value="bcd" />
<email value="qwe" />
</person>
I can unmarshal/marshal this class using own XmlAdapter implementation for each of field FirstNameAdapter, LastNameAdapter, EmailAdapter. As you can see each field represented in similar way - field name as xml element and field value as element's attribute. Is it possible to create "universal" adapter to which I'll be able to transfer name of the field and it will extract value of that field ?
P.S. I know about MOXy JAXB implementation, but I'm wondering if it is possible by means of reference JAXB implementation.
Thanks!
Upvotes: 4
Views: 1413
Reputation: 60858
You can use an XmlAdapter
like this:
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);
}
}
The idea here is that XML Schema and therefore also JAXB has a clear distinction between element names and content types, even though many documents have a clear one-to-one correspondence between these two. So in the above code, the type Valued
describes something that has a value
attribute, without regards for the element name. The members you want to serialize are annotated as @XmlElement
with no name included in that annotation. So they will generate elements with a name derived from the name of the member. The @XmlJavaTypeAdapter
annotation will cause the serializer to treat these members as if their types vere Valued
. So that is what their XML content type will be.
The schema for the above code looks like this:
<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>
Upvotes: 5
Reputation: 148977
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
P.S. I know about MOXy JAXB implementation, but I'm wondering if it is possible by means of reference JAXB implementation.
For comparisons sake, I have added how this could be done with EclipseLink JAXB (MOXy)'s @XmlPath
extension.
Person
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
To specify MOXy as your JAXB provider you need to add a file called jaxb.properties
in the same package as your domain model with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
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>
Upvotes: -1