Reputation: 931
I currently have the following two POJO classes.
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
@XmlElement
private String firstName;
@XmlElement
private String lastName;
@XmlElement
private String email;
/* snipped getters, setters, and constructors. */
}
@XmlRootElement(name = "testobj")
@XmlAccessorType(XmlAccessType.FIELD)
public class TestObj {
@XmlElement(name="data")
private String someData;
@XmlElement
private User user;
/* snipped getters, setters, and constructors. */
}
Current Output
<testObj>
<name>testObj</name>
<user>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>[email protected]</email>
</user>
</testObj>
Desired Output
<testObj>
<data>some data</data>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>[email protected]</email>
</testObj>
Is there any way I can achieve the desired output? The rationale for nesting the user object is that there are type heirarchies like so:
User
|-> Customer
|-> SpecialCustomer
DetailedUser
|-> DetailedCustomer
|-> DetailedSpecialCustomer
These six types are used specifically for holding data that will be outputted as the result of API calls. They have no logic in them. There is another more complex set of types that is used internally by the application, and a set of factories to convert the internal app types to the api output types. The API output types are serialized to XML and JSON using JAXB and Jackson. In order to avoid duplicated code, I want to use composition to embed the simple User|Customer|SpecialCustomer in the complex type like so:
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.FIELD)
public class DetailedUser {
@Xml[...?]
private User user;
@XmlElement
private String otherProperty;
@XmlElement
private String extraStuff;
/* snipped getters, setters, and constructors. */
}
When the DetailedUser is outputted through JAXB, it should look like so:
<user>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>[email protected]</email>
<otherProperty>something</otherProperty>
<extraStuff></extraStuff>
</user>
The object tree will be outputted in the appropriate format via Spring MVC using content negotiation.
Upvotes: 1
Views: 1452
Reputation: 149047
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
You could use MOXy's @XmlPath(".")
extension:
TestObj
package forum10844319;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@XmlRootElement(name = "testobj")
@XmlAccessorType(XmlAccessType.FIELD)
public class TestObj {
@XmlElement(name="data")
private String someData;
@XmlPath(".")
private User user;
/* snipped getters, setters, and constructors. */
}
User
package forum10844319;
import javax.xml.bind.annotation.*;
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.FIELD)
public class User {
@XmlElement
private String firstName;
@XmlElement
private String lastName;
@XmlElement
private String email;
/* snipped getters, setters, and constructors. */
}
jaxb.properties
To specify MOXy as your JAXB provider you need to include a file called jaxb.properties
in the same package as your domain model:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
package forum10844319;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(TestObj.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum10844319/input.xml");
TestObj testObj = (TestObj) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(testObj, System.out);
}
}
input.xml/Output
<testobj>
<data>some data</data>
<firstName>John</firstName>
<lastName>Doe</lastName>
<email>[email protected]</email>
</testobj>
Upvotes: 2