Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I marshall a single java bean into a complex XML document with existing annotations?

I have a single java bean, which is already annotated for JPA, that I also wish to store as XML, specifically FIXML. The goal is to manage the mapping from bean to XML with annotations.

I see related topics online about specifying a schema and letting JAXB generate classes, but I don't want to do that.

I've been looking at using JAXB annotations, but it seems that I'll need to make new classes for each child element. I'm trying to stay away from that, and let the annotations show how to construct the child elements. JAXB does not seem to want to do this.

Is this possible, and how? Do I need to make my own annotations and forget about JAXB?

Concrete example

Bean:

@Entity
@XmlRootElement(name="FIXML")
@XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
    private String account;
    private String senderCompID;

    @Column(name="ACCOUNT", nullable=true, length=64)
    @XmlAttribute(name="Acct")
    public String getAccount() {
        return this.account;
    }
    public void setAccount(String account) {
        this.account = account;
    }

    @Column(name="SENDER_COMP_ID", nullable=true, length=200)
    @XmlAttribute(name="SID")
    public String getSenderCompID() {
        return this.senderCompID;
    }
    public void setSenderCompID(String senderCompID) {
        this.senderCompID = senderCompID;
    }
}

Parsing:

JAXBContext context = JAXBContext.newInstance(ExecutionReport.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); //pretty print XML
marshaller.marshal(executionReport, System.out);

Desired resulting XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML>
  <ExecRpt Acct="account_data">
    <Hdr SID="sender"/>
  </ExecRpt>
</FIXML>

Current resulting XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<FIXML Acct="account_data" SID="sender"/>

Clearly I'm not providing enough information to map the child elements yet, but I'm also not sure how to provide it. I want to add some @XmlElement annotations, but I don't have child objects, all the data is in this class.

The upside is that my XML isn't much more complicated than this example; there are only a handful of elements, and they only appear once per message. The thing that's giving me trouble is getting multiple elements out of a single bean.

like image 213
Greg Chabala Avatar asked Jan 26 '11 22:01

Greg Chabala


People also ask

What is XML marshalling and Unmarshalling in Java?

Marshalling is the process of transforming Java objects into XML documents. Unmarshalling is the process of reading XML documents into Java objects. The JAXBContext class provides the client's entry point to the JAXB API. It provides API for marshalling, unmarshalling and validating.

What is @XmlType annotation in Java?

If class is annotated with @XmlType(name="") , it is mapped to an anonymous type otherwise, the class name maps to a complex type name. The XmlName() annotation element can be used to customize the name. Properties and fields that are mapped to elements are mapped to a content model within a complex type.

How do you ignore a field in XML response?

Ignore XML Attribute. You can specify ignore="true" or ignore="false". The default value is false. Specifying ignore="false" has no effect on the attribute value assigned when an object of the type specified in the rule is created and no effect on the constraints.


2 Answers

You can use the @XmlPath extension in EclipseLink JAXB (MOXy) for this, I'm the tech lead.

Model Class

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

import org.eclipse.persistence.oxm.annotations.XmlPath;

@Entity
@XmlRootElement(name="FIXML")
@XmlType(name="ExecRpt")
public class ExecutionReport implements Serializable {
    private String account;
    private String senderCompID;

    @Column(name="ACCOUNT", nullable=true, length=64)
    @XmlPath("ExecRpt/@Acct")
    public String getAccount() {
        return this.account;
    }
    public void setAccount(String account) {
        this.account = account;
    }

    @Column(name="SENDER_COMP_ID", nullable=true, length=200)
    @XmlPath("ExecRpt/Hdr/@SID")
    public String getSenderCompID() {
        return this.senderCompID;
    }
    public void setSenderCompID(String senderCompID) {
        this.senderCompID = senderCompID;
    }
}

Demo Code

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;

public class Demo {

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

        ExecutionReport er = new ExecutionReport();
        er.setAccount("account_data");
        er.setSenderCompID("sender");

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

}

Resulting XML

<?xml version="1.0" encoding="UTF-8"?>
<FIXML>
   <ExecRpt Acct="account_data">
      <Hdr SID="sender"/>
   </ExecRpt>
</FIXML>

Specifying the EclipseLink JAXB (MOXy) Implementation

To specify MOXy as the JAXB implementation you need to add a file called jaxb.properties in with your ExecutionReport class with the following entry:

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

For More Information

  • http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
like image 179
bdoughan Avatar answered Sep 28 '22 07:09

bdoughan


I don't know Jaxb annotations but if you ask for an attribute in ExecRpt it seems normal to have an attribute in ExecRpt no?

I think you expect a bit too much of these annotations. Don't you also want an annotation that would take a string, split it with a separator and generate a list of child elements or something?

And it seems to me a bad design to put these annotations directly on JPA entities. One day you could have to do some database changes for performances issues for exemple and you could not be able to generate the xml you want anymore. Why not transforming your jpa entity to a given structure jaxb friendly so that you keep the db and marshalling appart? Thus on change you would just have to modify the transformer.

like image 34
Sebastien Lorber Avatar answered Sep 28 '22 07:09

Sebastien Lorber