i have problem with my jaxb EclipseLink implementation.
Let's assume I have the following Entity ...
#XmlRootElement(name = GenericConfigEntity.XML_ROOT_TAG)
public class GenericConfigEntity {
private String name;
private String data;
private String version;
private String date;
private String template;
#XmlAttribute(name = GenericConfigEntity.XML_NAME)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement(name = GenericConfigEntity.XML_DATA)
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
#XmlAttribute(name = GenericConfigEntity.XML_VERSION)
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
#XmlAttribute(name = GenericConfigEntity.XML_DATE)
public String getDate() {
return date;
}
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
}
The string 'template' contains xml data already let's say someting like this (in my real context it is a lot more and I do not want to create the entities for this).
<Prozess name="xx" test="1">
<Debug system="test" />
</Prozess>
Now my question is if there is a way to integrate the template string into the marshalling process that someting like this is generated
<conf name="xx" version="x" datum="xx">
<Prozess name="xx" test="1">
<Debug system="test" />
</Prozess>
<Data>
TextTextText
</Data>
</conf>
It is no solution to wrap the template in an tag because i am restricted to this layout.
Also #XmlValue is no solution because I get an exception "all other elements have to be an attribute because one is marked as xmlvalue".
I haven't used this myself yet, but I reckon you could use the #XmlAnyElement in conjunction with a DomHandler to implement such a mapping. You can find a helpful example in the blog of MOXy's lead programmer here.
I think you'd still have to make sure that the XML content in your template field has at least an opening and a closing tag, which serve to identify the DOM element during (un)marshalling. That tag may be arbitrary as required by you. I guess you could just look for the first tag appearing in the string and try to match it against the end of the string.
Related
I don't quite understand how #XmlIDREF and #XmlID work together. By using XmlIDREF I only create a reference to the actual element. However what is the use case for XmlID.
I want to create a reference to the class Publication. Is it enough to annotate the publication List with #XmlIDREF?
public class Author {
private String id;
private String name;
private List<Publication> publications = new LinkedList<>();
public Author() {
super();
}
#XmlElement
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
#XmlIDREF
public List<Publication> getPublications() {
return publications;
}
I want to create a reference to the class Publication.
Is it enough to annotate the publication List with #XmlIDREF?
No, that's only one half of what you need.
You already have this:
With #XmlIDREF you mark the referencing side of the relation
(pointing from Author to Publication).
public class Author {
...
#XmlIDREF
#XmlElement(name = "publication")
public List<Publication> getPublications() {
return publications;
}
...
}
You also need to mark the referenced side (the Publication itself)
by annotating one of its properties with #XmlID, for example like this:
public class Publication {
...
#XmlID
#XmlElement
public String getId() {
return id;
}
...
}
Then you are able to process XML content like this example:
<root>
<publication>
<id>p-101</id>
<title>Death on the Nile</title>
</publication>
<publication>
<id>p-102</id>
<title>The murder of Roger Ackroyd</title>
</publication>
...
<author>
<id>a-42</id>
<name>Agatha Christie</name>
<publication>p-101</publication>
<publication>p-102</publication>
</author>
...
</root>
You see, the XML references (like <publication>p-101</publication>)
are mapped to Java object references (in List<Publication> publications).
I want the Country class to store the "ISO_3166-1_Alpha-2_Code" code. The code currently gets back the "ISO_3166-1_Numeric-3_Code" code. Can't figure out how to tweak the Country class to get the specific attribute I want.
XML:
<?xml version='1.0' encoding='UTF-8'?>
<wd:Message_Event_Configuration">
<wd:Message_Event_Configuration_Data>
<wd:Country_Reference wd:Descriptor="Saint Martin">
<wd:ID wd:type="WID">66b7082a21e510000961bb6d82b5002a</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">MF</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">MAF</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">663</wd:ID>
</wd:Country_Reference>
<wd:Country_Reference wd:Descriptor="Saint Barthelemy">
<wd:ID wd:type="WID">881527f6cec910000ba81e8dccf61127</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-2_Code">BL</wd:ID>
<wd:ID wd:type="ISO_3166-1_Alpha-3_Code">BLM</wd:ID>
<wd:ID wd:type="ISO_3166-1_Numeric-3_Code">652</wd:ID>
</wd:Country_Reference>
</wd:Message_Event_Configuration_Data>
</wd:Message_Event_Configuration>
Country List:
#XmlRootElement(name = "Message_Event_Configuration")
#XmlAccessorType(XmlAccessType.FIELD)
public class Countries {
#XmlElementWrapper(name = "Message_Event_Configuration_Data")
#XmlElement(name = "Country_Reference")
private List<Country> countries = new ArrayList<Country>();
public List<Country> getCountries() {
return countries;
}
public void setCountries(List<Country> countries) {
this.countries = countries;
}
}
Country:
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Country_Reference")
public class Country {
#XmlElement(name = "ID")
private String isoCode;
public Country() {
}
public Country(String isoCode) {
this.isoCode = isoCode;
}
#XmlAttribute(name = "ISO_3166-1_Alpha-2_Code")
public String getISOCode() {
return isoCode;
}
public void setISOCode(String isoCode) {
this.isoCode = isoCode;
}
}
The <Country_Reference> XML element contains the ISO codes in a rather
sophisticated way within several <wd:ID> XML elements.
It is therefore much too simple to model them as a Java String property.
Instead, you need to model the Java-structure with more similarity to the XML-structure.
The sequence of XML elements <wd:ID> can be modeled by a property List<ID> idList
which needs to be annotated by#XmlElement(name="ID") .
The XML attribute wd:Descriptor="...." can be modeled by a property String descriptor
which needs to be annotated by #XmlAttribute(name="Descriptor").
For your convenience you can add an all-arguments-constructor and some methods for getting
the WID and ISO codes from the List<ID>.
#XmlAccessorType(XmlAccessType.FIELD)
public class Country {
#XmlAttribute(name = "Descriptor")
private String descriptor;
#XmlElement(name = "ID")
private List<ID> idList;
public Country() {
}
public Country(String descriptor, String wid, String isoAlpha2Code, String isoAlpha3Code, String isoNumeric3Code) {
this.descriptor = descriptor;
idList = new ArrayList<>();
idList.add(new ID("WID", wid));
idList.add(new ID("ISO_3166-1_Alpha-2_Code", isoAlpha2Code));
idList.add(new ID("ISO_3166-1_Alpha-3_Code", isoAlpha3Code));
idList.add(new ID("ISO_3166-1_Numeric-3_Code", isoNumeric3Code));
}
public String getWid() {
return getIdByType("WID");
}
public String getIsoAlpha2Code() {
return getIdByType("ISO_3166-1_Alpha-2_Code");
}
public String getIsoAlpha3Code() {
return getIdByType("ISO_3166-1_Alpha-3_Code");
}
public String getIsoNumeric3Code() {
return getIdByType("ISO_3166-1_Numeric-3_Code");
}
private String getIdByType(String idType) {
for (ID id : idList) {
if (id.getType().equals(idType))
return id.getValue();
}
return null;
}
}
The XML elements <wd:ID> are quite complex. Therefore we need a separate POJO class for modeling them.
Let's call the class ID.
The XML text between <wd:ID ..> and </wd:ID> is modeled by the property String value
which needs to be annotated by #XmlValue.
The XML attribute wd:type="..." is modeled by the property String type
which needs to be annotated by #XmlAttribute.
For convenient use by the class Country above, an all-arguments-constructor is added.
#XmlAccessorType(XmlAccessType.FIELD)
public class ID {
#XmlAttribute
private String type;
#XmlValue
private String value;
public ID() {
}
public ID(String type, String value) {
this.type = type;
this.value = value;
}
// public getters and setters (omitted here fro brevity)
}
The screenshot below (taken from within the debugger) visualizes the Java structure
and confirms that the unmarshalling of your XML example works correctly:
I'm new in dealing with XML in Java, but one of services I use returns it as result. Until now, I've dealt with mapping XML into POJO, using #XmlRootElement-like annotations. But now I have absolutely no idea to do with this document:
<?xml version="1.0" encoding="windows-1251"?>
<response>
<status>
<code>0</code>
</status>
<result>
<limit>2</limit>
...
<data>
<row0>
<ID>85427</ID>
<name>Default</name>
<siteID>40628</siteID>
... some elements
</row0>
</data>
</result>
</response>
Until now, I used these classes to bind XML (except 'data' node) into POJO:
#XmlRootElement(name="response")
public class Response {
private Status status;
private String result;
public Status getStatus() {
return status;
}
#XmlElement(name = "status")
public void setStatus(Status status) {
this.status = status;
}
public String getResult() {
return result;
}
#XmlElement(name ="result")
public void setResult(String result) {
this.result = result;
}
}
#XmlRootElement(name = "status")
public class Status {
private String ID;
private String code;
private String error;
public String getID() {
return ID;
}
#XmlElement(name = "ID")
public void setID(String ID) {
this.ID = ID;
}
public String getCode() {
return code;
}
#XmlElement(name = "code")
public void setCode(String code) {
this.code = code;
}
public String getError() {
return error;
}
#XmlElement(name = "error")
public void setError(String error) {
this.error = error;
}
}
But now I need to bind content as collection of elements. I've looked for examples, and everywhere people use specific tag to define root element for collection's item, but in this document, root tags will be as <row0>, <row1> etc.
I use Jackson, which, if I understand correctly, uses JAXB annotations to define XML to POJO bind rules. So could this deal be solved this way, or I have to manipulate this document in DOM-style?
You can solve your problem by using something like this :
Create your Row class that represents your <row0>,<row1> etc... and map it with JAXB like you would do it normally.
Then create a class that extends XmlAdapter<List<Row>,List<Element>> and define the abstracts methods marshall and unmarshall.
Here is some Javadoc to help you :
XmlAdapter : http://docs.oracle.com/javaee/5/api/javax/xml/bind/annotation/adapters/XmlAdapter.html
Element : http://docs.oracle.com/javase/1.5.0/docs/api/org/w3c/dom/Element.html
Then create a Data class :
public class Data{
private List<Row> rows;
public List<Row> getRows() {
return rows;
}
#XmlAnyElement
#XmlJavaTypeAdapter(MyRowsAdapter.class)
public void setRows(List<Row> result) {
this.rows = rows;
}
}
Then you can add this mapping to your Response class :
private Data data;
public Data getData() {
return data;
}
#XmlElement(name="data")
public void setData(Data data) {
this.data = data;
}
Note that for this solution to work, your <data> element must only contains elements like your Row.
Also, you cannot use #XmlElementWrapper instead of using a Data class because of a bug in JAXB which make incompatible #XmlElementWrapper and #XmlJavaTypeAdapter : https://java.net/jira/browse/JAXB-787
I have a Java class which contains a List field.
public class Machine{
public int ID;
public String Name;
public String Status;
public String Environment;
public String Cluster;
public List<String> SupportedLocales;
}
Below is an instance of the class in XML that I am trying to unmarshall.
<?xml version="1.0" encoding="utf-8"?>
<Machine xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ID>27</ID>
<Name>QA14ADMINOPM201</Name>
<Status>Decom</Status>
<Environment>QA14</Environment>
<Cluster>ADMINOPM</Cluster>
<SupportedLocales>
<SupportedLocale>NA</SupportedLocale>
<SupportedLocale>Global</SupportedLocale>
</SupportedLocales>
</Machine>
When I unmarshall the Xml though, the resulting object has an empty SupportedLocales list, with no elements. Any reason why this might be happening or suggested fixes? All other fields are being unmarshalled correctly.
Annotate your field with an XmlElementWrapper annotation:
#XmlElementWrapper(name = "SupportedLocales")
#XmlElement(name = "SupportedLocale")
public List<String> SupportedLocales;
Also try to follow the convention of using variables that start with a lowercase (supportedLocales instead of SupportedLocales). This would need to map the XML element name for each field as follows:
#XmlElement(name="ID")
public int id;
#XmlElement(name="Name")
public String name;
#XmlElement(name="Status")
public String status;
#XmlElement(name="Environment")
public String environment;
#XmlElement(name="Cluster")
public String cluster;
#XmlElementWrapper(name = "SupportedLocales")
#XmlElement(name = "SupportedLocale")
public List<String> supportedLocales;
Consider the following xml:
<Config>
<Paths>
<Path reference="WS_License"/>
</Paths>
<Steps>
<Step id="WS_License" title="License Agreement" />
</Steps>
</Config>
The following JAXB classes:
public class Path {
private String _reference;
public String getReference() {
return _reference;
}
#XmlAttribute
public void setReference( String reference ) {
_reference = reference;
}
}
And
public class Step {
private String _id;
private String _title;
public String getId() {
return _id;
}
#XmlAttribute
public void setId( String id ) {
_id = id;
}
public String getTitle() {
return _title;
}
#XmlAttribute
public void setTitle( String title ) {
_title = title;
}
}
Instead of storing the reference in the Path object as String, I'd like to hold it as a Step object. The link between those objects is the reference and id attributes. Is the #XMLJavaTypeAdapter attribute the way to go? Could anyone be so kind to provide an example of the correct usage?
Thanks!
EDIT:
I'd also would like to do the same technique with an element.
Consider the following xml:
<Config>
<Step id="WS_License" title="License Agreement">
<DetailPanelReference reference="DP_License" />
</Step>
<DetailPanels>
<DetalPanel id="DP_License" title="License Agreement" />
</DetailPanels>
</Config>
The following JAXB classes:
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute(name="id")
private String _id;
#XmlAttribute(name="title")
private String _title;
#XmlIDREF
#XmlElement(name="DetailPanelReference", type=DetailPanel.class)
private DetailPanel[] _detailPanels; //Doesn't seem to work
}
#XmlAccessorType(XmlAccessType.FIELD)
public class DetailPanel {
#XmlID
#XmlAttribute(name="id")
private String _id;
#XmlAttribute(name="title")
private String _title;
}
The property _detailPanels in the Step-object is empty and the link doesn't seems to work. Is there any option to create a link without creating a new JAXB object holding only the reference to the DetailPanel?
Thanks again : )!
You can use #XmlID to map a property as the key and #XmlIDREF to map the reference to the key for this use case.
Step
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute
private String _id;
}
Path
#XmlAccessorType(XmlAccessType.FIELD)
public class Path {
#XmlIDREF
#XmlAttribute
private Step _reference;
}
For More Information
http://blog.bdoughan.com/2010/10/jaxb-and-shared-references-xmlid-and.html
UPDATE
Thanks! I Completely missed your article. I've extended my question,
do you have any clue if this is possible too? I do not want to create
a class with only holding the reference, I'd like to store it inside
the step class.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
If you are using MOXy as your JAXB (JSR-222) provider then you could leverage the #XmlPath annotation for your use case.
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlAccessorType(XmlAccessType.FIELD)
public class Step {
#XmlID
#XmlAttribute
private String id;
#XmlPath("DetailPanelReference/#reference")
#XmlIDREF
// private List<DetailPanel> _detailPanels; // WORKS
private DetailPanel[] _detailPanels; // See bug: http://bugs.eclipse.org/399293
}
For More Information
http://bugs.eclipse.org/399293
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html