1

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.

bdoughan
  • 147,609
  • 23
  • 300
  • 400
rocotocloc
  • 418
  • 6
  • 19

1 Answers1

0

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

bdoughan
  • 147,609
  • 23
  • 300
  • 400