javassist not injecting annotation at existing field - jaxb

I'm trying to inject JAXB annotation at runtime using Javassist. I have written following code:
public class AssistAnnotationInjector {
public static void addAnnotationRunTime(String className, String fieldName) throws NotFoundException, CannotCompileException, IOException, ClassNotFoundException{
CtClass ctClass = ClassPool.getDefault().get(className);
ClassFile ccFile = ctClass.getClassFile();
ConstPool constPool = ccFile.getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation("javax.xml.bind.annotation.XmlTransient",constPool);
attr.addAnnotation(annot);
CtField field = ctClass.getDeclaredField(fieldName);
field.getFieldInfo().addAttribute(attr);
System.out.println(field.getAnnotation(XmlTransient.class));
ccFile.setVersionToJava5();
ctClass.writeFile();
}
public static void main (String args[]) throws CannotCompileException, NotFoundException, IOException, SecurityException, NoSuchMethodException, ClassNotFoundException, JAXBException, NoSuchFieldException{
Person<Student> p = new Person<Student>();
p.setName("XYZ");
Student s = new Student();
s.setName("ABC");
s.setId("239423");
p.setPayload(s);
addAnnotationRunTime("RuntimeAnnotation.Person", "name");
Field f = p.getClass().getDeclaredField("name");
System.out.println(f.getAnnotation(XmlTransient.class));
JAXBContext context = JAXBContext.newInstance(p.getClass());
Marshaller mr = context.createMarshaller();
mr.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
mr.marshal(p, System.out);
}
}
And Person.java class is:
#XmlRootElement(name="Person")
#XmlAccessorType(XmlAccessType.FIELD)
#XmlSeeAlso({Student.class})
public class Person <T>{
private T payload;
private String name;
public void setPayload(T payload){
this.payload = payload;
}
public T getPayload(){
return payload;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
}
In AssistAnnotationInjector.java, I am trying to add XmlTransient annotation to 'name' field. But the name field is still coming in marshalling output. Why is it so?
PS: marshal output is :
#javax.xml.bind.annotation.XmlTransient
null
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Person>
<payload xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="student">
<name>ABC</name>
<id>239423</id>
</payload>
**<name>XYZ</name>**
</Person>
name tag was not expected to present in output..

You basicaly have 2 options:
do the modification before you load the class. You can not use reflection in the normal way! One can try to use org.reflections with Maven plugin to pre-fetch classes. See here for more info.
use custom classloader to load the modified class. See here for more info.

After adding the attribute to the field you need to call ctClass.toClass() method,which freezes the class. After this you can check for the annotation.

Related

How to persist UserData of Element in JAXB Unmarshal?

I unmarshal the Document to object as below.
Before that, when parsing XML, use setUserData to store location information for each element.
class MyJaxbAdapter extends XmlAdapter<Object, SubObject> {}
#Override
public UnattendComponent unmarshal(Object v) throws Exception {
Node node = (Node) v; // ElementNSImpl; It's probably a newly created object. Because It is different from the document object given by ownerDocument as SAXSource.
node.getUserData(...) // return NULL
}
}
Document document = ...;
unmarshaller.setAdapter(new MyJaxbAdapter());
MyXMLObject object = unmarshaller.unmarshal(new DOMSource(document), MyXMLObject.class).getValue();
But I can't get UserData inside XmlAdapter's unmarshal method. Is there any way to persist UserData?
Locator information is stored in the properties of Element as shown below.
#Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
Element el = document.createElementNS(usedNamespaceUri, qName);
// ...
el.setUserData(
ElementUserData.class.getName(),
ElementUserData.builder()
.lineNumber(locator.getLineNumber())
.columnNumber(locator.getColumnNumber())
.build(),
null);
}
I need the Locator information (UserData) stored by the above code in the unmarshal of the XmlAdapter.
However, there is no userdata in the node passed as an argument to unmarshal .
Sample Code:
https://github.com/joseph-jclab/jaxb-question-01
Not entirely sure if this is something you are looking for but providing it as a reference so you might get some idea to proceed further:
Sample XML:
<root>
<name>Batman</name>
<year>2008</year>
</root>
Root.class:
#XmlRootElement(name = "root")
#Data
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
private String name;
private String year;
#XmlJavaTypeAdapter(CustomAdapter.class)
private String after;
private void afterUnmarshal(Unmarshaller m, Object parent) {
after = name;
}
}
CustomAdapter.class:
public class CustomAdapter extends XmlAdapter<String, String> {
#Override
public String unmarshal(String v) throws Exception {
System.out.println("Within Unmarshal : " + v);
return null;
}
#Override
public String marshal(String v) throws Exception {
System.out.println("Within Marshal : " + v);
return null;
}
}
SampleMain.class:
public class SampleMain {
public static void main(String[] args) throws XMLStreamException, JAXBException {
final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("sample.xml");
final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
final Unmarshaller unmarshaller = JAXBContext.newInstance(Root.class).createUnmarshaller();
unmarshaller.setAdapter(new CustomAdapter());
final Root root = unmarshaller.unmarshal(xmlStreamReader, Root.class).getValue();
System.out.println(root.toString());
Marshaller marshaller = JAXBContext.newInstance(Root.class).createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(root, System.out);
}
}
Output:
Root(name=Batman, year=2008, after=Batman)
Within Marshal : Batman
<root>
<name>Batman</name>
<year>2008</year>
</root>

JAXB - how to indicate duplicate nodes when unmarshalling

I have a question. Is there any way to detect duplicate nodes (with different values) in xml file?
If java class looks like this (I've just made this up for sake of simplicity):
#XmlRootElement(name = "PERSON")
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
#XmlElement(name = "NAME")
private String name;
#XmlElement(name = "SURNAME")
private String surname;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
}
and input .xml file looks like this:
<person>
<name>John</name>
<surname>Smith</surname>
<name>Jack</name>
</person>
After unmarshalling process, value 'John' gets overriden by value 'Jack'.
Is there any way to detect/avoid this?
(So i can throw Exception or log occurence of this duplicate?)
The general solution to this problem is to set your XML schema as an instance of javax.xml.validation.Schema on that Unmarshaller to ensure the XML matches the expected input.
For More Information
http://blog.bdoughan.com/2010/12/jaxb-and-marshalunmarshal-schema.html

xs:int unmarshalled to null for decimal values

I've got a problem related to the unmarshalling process in a JAX-WS based WebService. In WSDL file there is an element defined as
<element name="quantity" nillable="true" type="int" />
In the related JAVA class it is defined as:
#XmlElement(name = "Quantity", required = true, type = Integer.class, nillable = true)
protected Integer quantity;
When an XML value for this element is the representation of a decimal number (3.4), the element is unmarshalled as a null Integer. No SOAPFault is generated and it's impossible to distinguish decimal values from null values inside the WebService.
Could it be a defect in JAXB implementation or I'm doing something wrong?
Could it be a defect in JAXB implementation or I'm doing something
wrong?
This is not a defect in the JAXB (JSR-222) implementation. It is a result of how the JAX-WS is configured to use JAXB. I will demonstrate below with an example.
Root
Below is a domain object with a field that matches the one from your question. I have remove the type=Integer.class from the #XmlElement annotation since it is redundant.
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
#XmlElement(name = "Quantity", required = true, nillable = true)
protected Integer quantity;
}
Demo
JAXB offers the ability to set a ValidationEventHandler on the Unmarshaller to give you some control over how unmarshal errors are handled.
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setEventHandler(new ValidationEventHandler() {
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return true;
}
});
StringReader xml = new StringReader("<root><Quantity>3.4</Quantity></root>");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
Output
In the expert group we decided that invalid element data is common and that JAXB should not fail out every time this is encountered, but you can see that a ValidationEvent is raised.
Not a number: 3.4
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<Quantity xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>
Update Demo
If we update the ValidationEventHandler to indicate that we do not wish to continue the unmarshal when a ValidationEvent is raised we can make the following change.
#Override
public boolean handleEvent(ValidationEvent event) {
System.out.println(event.getMessage());
return false;
}
Updated Output
And now the following output occurs.
Not a number: 3.4
Exception in thread "main" javax.xml.bind.UnmarshalException: Not a number: 3.4
- with linked exception:
[java.lang.NumberFormatException: Not a number: 3.4]
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:676)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleError(UnmarshallingContext.java:672)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader.handleParseConversionException(Loader.java:256)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:54)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.text(UnmarshallingContext.java:499)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.processText(SAXConnector.java:166)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.endElement(SAXConnector.java:139)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:606)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1742)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2900)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:607)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:116)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:489)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:835)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:123)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1210)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:568)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:203)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:175)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:157)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:214)
at forum14741140.Demo.main(Demo.java:22)
Caused by: java.lang.NumberFormatException: Not a number: 3.4
at com.sun.xml.internal.bind.DatatypeConverterImpl._parseInt(DatatypeConverterImpl.java:101)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:713)
at com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl$17.parse(RuntimeBuiltinLeafInfoImpl.java:711)
at com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor$CompositeTransducedAccessorImpl.parse(TransducedAccessor.java:232)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyLoader.text(LeafPropertyLoader.java:50)
... 19 more
I answered this question in : https://stackoverflow.com/a/30617814/3632201
I have been struggling with this issue during the last week and finally i have managed a working solution. The trick is that JAXB looks for the methods beforeUnmarshal and afterUnmarshal in the object annotated with #XmlRootElement.
..
#XmlRootElement(name="MSEPObtenerPolizaFechaDTO")
#XmlAccessorType(XmlAccessType.FIELD)
public class MSEPObtenerPolizaFechaDTO implements Serializable {
..
public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException, IOException, SAXException {
unmarshaller.setSchema(Utils.getSchemaFromContext(this.getClass()));
unmarshaller.setEventHandler(new CustomEventHandler());
}
public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) throws JAXBException {
unmarshaller.setSchema(null);
unmarshaller.setEventHandler(null);
}
Using this ValidationEventHandler:
public class CustomEventHandler implements ValidationEventHandler{
#Override
public boolean handleEvent(ValidationEvent event) {
if (event.getSeverity() == event.ERROR ||
event.getSeverity() == event.FATAL_ERROR)
{
ValidationEventLocator locator = event.getLocator();
throw new RuntimeException(event.getMessage(), event.getLinkedException());
}
return true;
}
}
}
And this is the metodh getSchemaFromContext created in your Utility class:
#SuppressWarnings("unchecked")
public static Schema getSchemaFromContext(Class clazz) throws JAXBException, IOException, SAXException{
JAXBContext jc = JAXBContext.newInstance(clazz);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
jc.generateSchema(new SchemaOutputResolver(){
#Override
public Result createOutput(String namespaceUri,
String suggestedFileName) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
outs.add(out);
StreamResult streamResult = new StreamResult(out);
streamResult.setSystemId("");
return streamResult;
}
});
StreamSource[] sources = new StreamSource[outs.size()];
for (int i = 0; i < outs.size(); i++) {
ByteArrayOutputStream out = outs.get(i);
sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()), "");
}
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
return sf.newSchema(sources);
}

Hide a value in xml that can be retrieved only when unmarshalling

Is it possible to hide a value in XML such that it can be retrieved only when unmarshalling.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
Below are some options for implementing this use case if you are using MOXy as your JAXB provider. To use MOXy as your JAXB provider you need to include a file named jaxb.properties in the same package as your domain model with the following entry:
OPTION #1 - XmlAdapter
An XmlAdapter could be used to null out a property value during the marshal operation. While the XmlAdapter is a standard JAXB class, returning null from the marshal method causes an exception to occur when the JAXB reference implementation is used.
StringAdapter
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringAdapter extends XmlAdapter<String, String> {
#Override
public String marshal(String string) throws Exception {
return null;
}
#Override
public String unmarshal(String string) throws Exception {
return string;
}
}
Person
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name="Person")
public class Person {
String password;
#XmlJavaTypeAdapter(StringAdapter.class)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
OPTION #2 - Setter With No Getter
When MOXy is used as the JAXB provider if you have a property with an annotated setter and no getter then MOXy will treat it as a readonly property.
import javax.xml.bind.annotation.*;
#XmlRootElement(name="Person")
public class Person {
String password;
#XmlElement
public void setPassword(String password) {
this.password = password;
}
}
OPTION #3 - MOXy's #XmlReadOnly Extension
MOXy's #XmlReadOnly extension can also be used to mark a property as read only.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlReadOnly;
#XmlRootElement(name="Person")
public class Person {
String password;
#XmlReadOnly
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
DEMO CODE
input.xml
<Person>
<password> some password </password>
</Person>
*Demo*
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/forum14231799/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
System.out.println(person.password);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(person, System.out);
}
}
Output
some password
<?xml version="1.0" encoding="UTF-8"?>
<Person/>
The following approach could be used with any JAXB (JSR-222) implementation.
Demo
A Marshaller.Listener could be used to null out a value before the object is marshalled and then restore it afterwards.
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/forum14231799/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
System.out.println(person.password);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setListener(new Marshaller.Listener() {
private String password;
#Override
public void afterMarshal(Object object) {
if(object instanceof Person) {
Person person = (Person) object;
person.setPassword(password);
password = null;
}
}
#Override
public void beforeMarshal(Object object) {
if(object instanceof Person) {
Person person = (Person) object;
password = person.getPassword();
person.setPassword(null);
}
}
});
marshaller.marshal(person, System.out);
System.out.println(person.password);
}
}
input.xml
<Person>
<password> some password </password>
</Person>
Output
some password
<?xml version="1.0" encoding="UTF-8"?>
<Person/>
some password

MOXy JAXB: how to map several XML tag elements to the same JAVA bean property

I am trying to unmarshall an XML file using MOXy JAXB. I have a set of classes, already generated, and I am using Xpath to map every XML element I need into my model.
I have an XML file like this:
<?xml version="1.0" encoding="UTF-8"?>
<fe:Facturae xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
xmlns:fe="http://www.facturae.es/Facturae/2009/v3.2/Facturae">
<Parties>
<SellerParty>
<LegalEntity>
<CorporateName>Company Comp SA</CorporateName>
<TradeName>Comp</TradeName>
<ContactDetails>
<Telephone>917776665</Telephone>
<TeleFax>917776666</TeleFax>
<WebAddress>www.facturae.es</WebAddress>
<ElectronicMail>facturae#mityc.es</ElectronicMail>
<ContactPersons>Fernando</ContactPersons>
<CnoCnae>28000</CnoCnae>
<INETownCode>2134AAB</INETownCode>
<AdditionalContactDetails>Otros datos</AdditionalContactDetails>
</ContactDetails>
</LegalEntity>
</SellerParty>
<BuyerParty>
<Individual>
<Name>Juana</Name>
<FirstSurname>MauriƱo</FirstSurname>
<OverseasAddress>
<Address>Juncal 1315</Address>
<PostCodeAndTown>00000 Buenos Aires</PostCodeAndTown>
<Province>Capital Federal</Province>
<CountryCode>ARG</CountryCode>
</OverseasAddress>
<ContactDetails>
<Telephone>00547775554</Telephone>
<TeleFax>00547775555</TeleFax>
</ContactDetails>
</Individual>
</BuyerParty>
</Parties>
</fe:Facturae>
Then I have my model:
#XmlRootElement(namespace="http://www.facturae.es/Facturae/2009/v3.2/Facturae", name="Facturae")
public class Facturae implements BaseObject, SecuredObject, CreationDataAware {
#XmlPath("Parties/SellerParty")
private Party sellerParty;
#XmlPath("Parties/BuyerParty")
private Party buyerParty;
}
public class Party implements BaseObject, SecuredObject, CreationDataAware {
#XmlPath("LegalEntity/ContactDetails")
private ContactDetails contactDetails;
}
As you can see, <ContactDetails></ContactDetails> is present in <SellerParty></SellerParty> and <BuyerParty></BuyerParty> but this two tags share the same JAVA object (Party). With the previous mapping (#XmlPath("LegalEntity/ContactDetails")) I can pass correctly the ContactDetails info in SellerParty, but I want also to pass the ContactDetails in <BuyerParty> at the same time.
I was trying something like that:
#XmlPaths(value = { #XmlPath("LegalEntity/ContactDetails"),#XmlPath("Individual/ContactDetails") })
private ContactDetails contactDetails;
but it doesn't work.
Can you guys give me a hand?
Thank you very much.
You could use an XmlAdapter for this use case:
PartyAdapter
We will use an XmlAdapter to convert an instance of Party to another type of object AdaptedParty. AdaptedParty will have two properties corresponding to each property in Party (one for each mapping possibility). We will take advantage of the ability to pre-initialize an instance of XmlAdapter to set an instance of Facturae on it. We will use the instance of Facturae to determine if the instance of Party we are handling is a sellerParty or a buyerParty.
package forum9807536;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
public class PartyAdapter extends XmlAdapter<PartyAdapter.AdaptedParty, Party> {
private Facturae facturae;
public PartyAdapter() {
}
public PartyAdapter(Facturae facturae) {
this.facturae = facturae;
}
#Override
public Party unmarshal(AdaptedParty v) throws Exception {
Party party = new Party();
if(v.individualName != null) {
party.setName(v.individualName);
party.setContactDetails(v.individualContactDetails);
} else {
party.setName(v.sellPartyName);
party.setContactDetails(v.sellerPartyContactDetails);
}
return party;
}
#Override
public AdaptedParty marshal(Party v) throws Exception {
AdaptedParty adaptedParty = new AdaptedParty();
if(null == facturae || facturae.getSellerParty() == v) {
adaptedParty.sellPartyName = v.getName();
adaptedParty.sellerPartyContactDetails = v.getContactDetails();
} else {
adaptedParty.individualName = v.getName();
adaptedParty.individualContactDetails = v.getContactDetails();
}
return adaptedParty;
}
public static class AdaptedParty {
#XmlPath("Individual/Name/text()")
public String individualName;
#XmlPath("Individual/ContactDetails")
public ContactDetails individualContactDetails;
#XmlPath("LegalEntity/CorporateName/text()")
public String sellPartyName;
#XmlPath("LegalEntity/ContactDetails")
public ContactDetails sellerPartyContactDetails;
}
}
Party
We will use the #XmlJavaTypeAdapter to associate the PartyAdapter to the Party class:
package forum9807536;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlJavaTypeAdapter(PartyAdapter.class)
public class Party implements BaseObject, SecuredObject, CreationDataAware {
private String name;
private ContactDetails contactDetails;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ContactDetails getContactDetails() {
return contactDetails;
}
public void setContactDetails(ContactDetails contactDetails) {
this.contactDetails = contactDetails;
}
}
Demo
The following demo code demonstrates how to set a pre-initialized XmlAdapter on the Marshaller:
package forum9807536;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Facturae.class);
File xml = new File("src/forum9807536/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Facturae facturae = (Facturae) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setAdapter(new PartyAdapter(facturae));
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(facturae, System.out);
}
}
Output
Below is the output from the demo code that corresponds to the portion of your model that I have mapped.
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Facturae xmlns:ns0="http://www.facturae.es/Facturae/2009/v3.2/Facturae">
<Parties>
<BuyerParty>
<Individual>
<Name>Juana</Name>
<ContactDetails/>
</Individual>
</BuyerParty>
<SellerParty>
<LegalEntity>
<CorporateName>Company Comp SA</CorporateName>
<ContactDetails/>
</LegalEntity>
</SellerParty>
</Parties>
</ns0:Facturae>
For More Information
http://blog.bdoughan.com/search/label/XmlAdapter
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

Resources