I have the following XML format:
<repositories>
<set>
<id>1</id>
<name>First</name>
<spec>data</spec>
</set>
<set>
<id>2</id>
<name>INFO</name>
<spec>main</spec>
</set>
.
.
</repositories>
I create the following package-info.java
#javax.xml.bind.annotation.XmlSchema (
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns = {}
)
package website.model;
import javax.xml.bind.annotation.XmlNsForm;
ANd the following classes:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Repositories {
#XmlElement
private ListofRepositories repositories;
public ListofRepositories getRepositories() {
return repositories;
}
public void setRepositories(ListofRepositories repositories) {
this.repositories = repositories;
}
}
Wrap the sets
#XmlAccessorType(XmlAccessType.FIELD)
public class ListofRepositories {
private List<Sets> set;
public List<Sets> getSet() {
return set;
}
public void setSet(List<Sets> set) {
this.set = set;
}
}
And the data:
#XmlAccessorType(XmlAccessType.FIELD)
public class Sets {
private Long id;
private String name;
private String spec;
//get set
}
I don't know why this doesn't work. The response is always null. I implemented similar processes with Java and JAXB annotations and i never had this kind of problem. Does anyone knows what is wrong and how can i fix it?
For the above xml, you need following class structure:
#XmlRootElement(name="repositories")
#XmlAccessorType(XmlAccessType.FIELD)
public class Repositories {
#XmlElement
private List<Sets> set;
//getter and setter
}
#XmlAccessorType(XmlAccessType.FIELD)
public class Sets {
private Long id;
private String name;
private String spec;
//getter and setter
}
But, according to your class structure you will get following xml:
<Repositories>
<repositories>
<set>
<id></id>
<name></name>
<spec></spec>
</set>
<set>
<id></id>
<name></name>
<spec></spec>
</set>
.
.
.
</repositories>
</Repositories>
with JAXB you could also generate these classes from an xsd file -- and check the incoming xml against the xsd (which will show why it will not accept it)
Related
I am trying to use JAXB to marshal class file(with annotations). Under <profile-set> it can have different tags for e.g.
<organization-information-profile>
<connection-profile>
<user-information-profile>
Sample output XML files are as below
a)
<?xml version="1.0"?>
<request version="2.0" principal="111" credentials="xxxxx">
<target name="TestAPI" operation="create">
<parameter>
<organization>
<qualified-name>some-qualified-name</qualified-name>
<profile-set>
<name>TestOrg</name>
<organization-information-profile>
<name>Organization Information</name>
<qualified-name>/Organization Information</qualified-name>
<last-name>Test</last-name>
<address>some-address</address>
<city>my-city</city>
<province></province>
<postal-code>1111</postal-code>
<country>Timbaktu</country>
<phone-number-day>1111</phone-number-day>
<email-address>some#email.com</email-address>
<attribute name="PhoneNumber1">
<value context="organization">23333</value>
</attribute>
<attribute name="ShortName">
<value context="organization">my company</value>
</attribute>
<attribute name="TaxId">
<value context="organization">myorg</value>
</attribute>
</organization-information-profile>
</profile-set>
</organization>
</parameter>
</target>
</request>
b)
<?xml version="1.0"?>
<request version="2.0" principal="11111" credentials="xxxxx">
<target name="TestAPI" operation="update">
<parameter>
<organization>
<qualified-name>some-qualified-name</qualified-name>
<profile-set>
<name>TestOrg</name>
<connection-profile>
<qualified-name>some-qualified-name</qualified-name>
<service>
<name>some service</name>
</service>
<attribute name="att-1">
<value context="organization" segment="some-segment" subscript="524288">fill-the-value</value>
</attribute>
<attribute name="att-2">
<value context="organization" segment="some-segment" subscript="524288">qedqwe</value>
</attribute>
</connection-profile>
</profile-set>
</organization>
</parameter>
</target>
</request>
Below is the code (only profile-set)
public static class ProfileSet
{
#XmlElement(name = "name")
public String name;
// innerPayLoad is template to store different profile objects
#XmlJavaTypeAdapter(CustomAdaptor.class)
#XmlElement
public InnerPayLoad innerPayLoad;
public ProfileSet(String name, InnerPayLoad innerPayLoad)
{
this.name = name;
this.innerPayLoad = innerPayLoad;
}
}
And CustomAdaptor
public class CustomAdaptor extends XmlAdapter<String,InnerPayLoad<?>>
{
#Override
public InnerPayLoad<?> unmarshal(String v) throws Exception
{
return null;
}
#Override
public String marshal(InnerPayLoad<?> v) throws Exception
{
String value = TestCode.convertToXmlNoHeader(v.whichProfile,v.whichProfile.getClass());
// after converting value becomes
// <organization-information-profile>
// <name>Organization Information</name>
// </organization-information-profile>
return value;
}
}
But the final XML produced is not similar to (a) for organization-information-profile
<?xml version='1.0' encoding='UTF-8'?>
<request version="2.0" principle="11111" credentials="xxxxx">
<target name="TestAPI" operation="create">
<parameter>
<organization>
<qualified-name>newOrg</qualified-name>
<profile-set>
<innerPayLoad><organization-information-profile>
<name>Organization Information</name>
</organization-information-profile></innerPayLoad>
<name>testOrg</name>
</profile-set>
</organization>
</parameter>
</target>
</request>
Is it possible to remove <innerPayLoad> tag and just insert with CustomAdaptor marshal function return value?
Appreciate help and hints to solve this issue.
You don't need to write a custom adapter for the various profile types within your ProfileSet.
Instead, to handle such mixed XML Content the canonical approach goes like this.
In your ProfileSet class you should define a polymorphic Java property profile
which can take the contents of a <organization.information-profile>,
<connection-profile> or <user-information-profile> element.
(I preferred the name profile here instead of innerPayload).
The mapping between these XML element names and Java classes is done
by using the #XmlElements annotation.
#XmlAccessorType(XmlAccessType.FIELD)
public class ProfileSet {
#XmlElement(name = "name")
private String name;
// template to store different profile objects
#XmlElements({
#XmlElement(name = "organization-information-profile", type = OrganizationInfomationProfile.class),
#XmlElement(name = "connection-profile", type = ConnectionProfile.class),
#XmlElement(name = "user-information-profile", type = UserInformationProfile.class)
})
private Profile profile;
// default constructor used by JAXB unmarshaller
public ProfileSet() {
}
public ProfileSet(String name, Profile profile) {
this.name = name;
this.profile = profile;
}
}
You need an abstract super-class Profile containing only the properties common to all kinds of profiles:
#XmlAccessorType(XmlAccessType.FIELD)
public abstract class Profile {
#XmlElement
private String name;
#XmlElement(name = "attribute")
private List<Attribute> attributes;
}
You have one subclass OrganizationInformationProfile for representing the
<organization-information-profile> element
#XmlAccessorType(XmlAccessType.FIELD)
public class OrganizationInfomationProfile extends Profile {
#XmlElement(name = "qualified-name")
private String qualifiedName;
#XmlElement(name = "last-name")
private String lastName;
#XmlElement(name = "address")
private String address;
// ... other properties
}
and another subclass ConnectionProfile for representing the <connection-profile> element
#XmlAccessorType(XmlAccessType.FIELD)
public class ConnectionProfile extends Profile {
#XmlElement(name = "service")
private Service service;
}
and yet another subclass UserInformationProfile for representing the <user-information-profile> element.
By using the above approach you can unmarshal your XML examples
and get the same output again when marshalling.
I want to give custom names to the xml root element and to list elements.
But the annotations don't work.
#XmlRootElement(name = "test")
#XmlAccessorType(XmlAccessType.FIELD)
public class TestRsp {
#XmlElementWrapper(name = "persons")
#XmlElement(name = "pax")
private List<Person> persons;
}
public class Person {
private String name;
private String age;
}
Usage:
#RestController
public class MyServlet {
#RequestMapping("/test")
public TestRsp test() {
//...
return rsp;
}
}
Result:
<TestRsp> <!-- should be named "test" -->
<persons>
<persons> <!-- should be named "pax" -->
<name />
<age />
</persons>
<persons>
//...
</persons>
</persons>
</TestRsp>
So my xml annotations are not picked up. But why?
Try using #JsonProperty annotation.
It should look something like thit:
#JsonProperty("test")
EDIT 1:
Try annotating the getter of the fields that you want to change like so:
#XmlElement(name="someName")
EDIT 2:
#XmlRootElement(name="persons")
public class Root {
private List<String> someList;
#XmlElement(name="pax")
public List<String> getSomeList() {
return someList;
}
public void setSomeList(List<String> someList) {
this.someList = someList;
}
public Root(String numValue,List<String> someListValue) {
this();
this.number = numValue;
this.someList = someListValue;
}
/**
*
*/
public Root() {
// TODO Auto-generated constructor stub
}
}
Maybe this will provide:
<persons>
<pax>FOO</pax>
<pax>BAR</pax>
</persons>
EDIT 3:
Maybe if you want to make your solution works you need to add a file called jaxb.properties in with your model classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
It turned out I have to use #JacksonXml* annotations instead:
#JacksonXmlRootElement(localName = "test")
public class TestRsp {
#JacksonXmlElementWrapper(localName = "persons")
#JacksonXmlProperty(localName = "pax")
#JsonProperty(name = "persons")
private List<Person> persons;
}
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
I need to convert one POJO into the following XML:
<Root>
<Version>2.0</Version>
<Name>John</Name>
<Age>18</Age>
<UserId>22491</UserId>
<Country>USA</Country>
<AnotherData>
<Records>
<AnotherRecord>
<Field1>XXX</Field1>
<Field2>XX</Field2>
<Field3>CCCCCCCC</Field3>
<Field4>XXX9000</Field4>
<Field5>XXX00345</Field5>
</AnotherRecord>
</Records>
</AnotherData>
</Root>
I know how to convert the fields below the root tag, it's not a problem. But from the AnotherData my problem's starting.
To represent the xml above I need some class like this:
puclic class Root{
public String Version;
public String Name;
public String Age;
public String UserID;
public String Country;
public AnotherData AnotherData;
}
public class AnotherData{
public Records Records;
}
public class Records{
List<AnotherRecord> list;
}
public class AnotherRecord{
public String Field1;
public String Field2;
public String Field3;
public String Field4;
public String Field5;
}
But I don't need of this structure of class, I like implement my classes in a more simple mode, and "force" the tag structure in xml.
My class would be like below, but keeping the structure xml like above.
puclic class Root{
public String Version;
public String Name;
public String Age;
public String UserID;
public String Country;
public AnotherData AnotherData;
List<AnotherRecord> list;
}
public class AnotherRecord{
public String Field1;
public String Field2;
public String Field3;
public String Field4;
public String Field5;
}
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I do not believe this use case can be handled by XStream. If you are open to using other technologies, below is an example of how it could be done with MOXy. Using the #XmlPath extension.
#XmlPath("AnotherData/Records/AnotherRecord")
List<AnotherRecord> list;
Root
Below is what the fully mapped Root class would look like. JAXB does not require any annotations (see: http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html), but since the XML elements in your document do not match the default naming rules some annotations are required.
package forum11970410;
import java.util.List;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="Root")
public class Root{
#XmlElement(name="Version")
public String Version;
#XmlElement(name="Name")
public String Name;
#XmlElement(name="Age")
public String Age;
#XmlElement(name="UserId")
public String UserID;
#XmlElement(name="Country")
public String Country;
#XmlPath("AnotherData/Records/AnotherRecord")
List<AnotherRecord> list;
}
AnotherRecord
package forum11970410;
import javax.xml.bind.annotation.XmlElement;
public class AnotherRecord{
#XmlElement(name="Field1")
public String Field1;
#XmlElement(name="Field2")
public String Field2;
#XmlElement(name="Field3")
public String Field3;
#XmlElement(name="Field4")
public String Field4;
#XmlElement(name="Field5")
public String Field5;
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file called jaxb.properties in the same package as your domain classes with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
You can use the following demo code to prove that everything works:
package forum11970410;
import java.io.File;
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();
File xml = new File("src/forum11970410/input.xml");
Root root = (Root) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<Version>2.0</Version>
<Name>John</Name>
<Age>18</Age>
<UserId>22491</UserId>
<Country>USA</Country>
<AnotherData>
<Records>
<AnotherRecord>
<Field1>XXX</Field1>
<Field2>XX</Field2>
<Field3>CCCCCCCC</Field3>
<Field4>XXX9000</Field4>
<Field5>XXX00345</Field5>
</AnotherRecord>
</Records>
</AnotherData>
</Root>
For More Information
http://blog.bdoughan.com/2010/09/xpath-based-mapping-geocode-example.html
http://blog.bdoughan.com/2010/10/how-does-jaxb-compare-to-xstream.html
I have this two model.
AssetMetadata:
#XmlRootElement(name="AssetMetadata")
public class AssetMetadata {
private AssetMetadataType assetMetadataType;
private String id;
private String assetId;
....
AssetMetadataType:
#XmlRootElement(name = "AssetMetadataType")
public class AssetMetadataType {
private String id;
private String name;
....
I use the JaxB unmarshaller like this.
spring config:
<oxm:jaxb2-marshaller id="marshaller">
<oxm:class-to-be-bound name="ch.srf.esb.radioimporter.domain.AssetMetadata"/>
<oxm:class-to-be-bound name="ch.srf.esb.radioimporter.domain.AssetMetadataType"/>
</oxm:jaxb2-marshaller>
Java code:
#Autowired #Qualifier("marshaller") private Unmarshaller unmarshaller;
...
final InputStream is = new ByteArrayInputStream(xml.getBytes());
this.unmarshaller.unmarshal(new StreamSource(is));
Now when I send the following XML, the AssetMetadataType is not set:
<AssetMetadata>
<AssetMetadataType>
<id>1</id>
<name>EPG</name>
</AssetMetadataType>
<assetId>39b4864d-931b-40c6-85ad-c45251b97952</assetId>
<title>title</title>
<description>description</description>
</AssetMetadata>
What do I do wrong ?
#XmlRootElement should only be set on the root element. That's why it's called #XmlRootElement. It'll be ignored anywhere else.
Try removing #XmlRootElement from the AssetMetadataType class, and change the property in AssetMetadata to be:
#XmlElement(name="AssetMetadataType")
private AssetMetadataType assetMetadataType;