Parse xml based on attributes using JAXB - java

I am using JAXB to parse some xml.
<countries>
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
</countries>
If I in my class Countries use
#XmlElement(name = "Name", required = true)
protected List<Name> name;
everything works.
However I would like to only get the attribute where language="en"
So I in my Countries class have
protected String name
not a collection.
Is there a good way to solve this with some annotation for example?

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group
Below are two ways you could handle this use case. The first is a little more code but could be done with any JAXB implementation. The second is less code, but requires you use EclipseLink JAXB (MOXy).
OPTION #1 - ANY JAXB (JSR-222) IMPLEMENTATION
Demo
You could use a filtered stream reader to filter out the unwanted elements and have your JAXB implementation unmarshal that.
package forum11586106;
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
private static final String LANGUAGE_CODE = "en";
public static void main(String[] args) throws Exception {
XMLInputFactory xif = XMLInputFactory.newFactory();
XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource("src/forum11586106/input.xml"));
xsr = xif.createFilteredReader(xsr, new StreamFilter() {
private boolean isReading = true;
#Override
public boolean accept(XMLStreamReader reader) {
if(reader.isStartElement() && "Name".equals(reader.getLocalName())) {
isReading = LANGUAGE_CODE.equals(reader.getAttributeValue("", "language"));
return isReading;
} else if(reader.isEndElement() && !isReading) {
isReading = true;
return false;
}
return true;
}});
JAXBContext jc = JAXBContext.newInstance(Countries.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Countries countries = (Countries) unmarshaller.unmarshal(xsr);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(countries, System.out);
}
}
Countries
package forum11586106;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Countries {
private String name;
#XmlElement(name="Name")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
input.xml
With this approach the language attribute is not included in the output:
<countries>
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
</countries>
Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<countries>
<Name>Australia</Name>
</countries>
OPTION #2 - ECLIPSELINK JAXB (MOXy)
We will leverage MOXy's #XmlPath extension to map to the Name element that has a language attribute with value en (see http://blog.bdoughan.com/2011/03/map-to-element-based-on-attribute-value.html).
Countries
package forum11586106;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement
public class Countries {
private String name;
#XmlPath("Name[#language='en']/text()")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model 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
With this approach the element filtering is handled by the #XmlPath mapping, so the runtime portion becomes much simpler. Note how only the standard JAXB runtime APIs are used.
package forum11586106;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Countries.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11586106/input.xml");
Countries countries = (Countries) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(countries, System.out);
}
}
input.xml
<countries>
<Name language="en">Australia</Name>
<Name language="se">Australien</Name>
</countries>
Output
With this approach the language attribute is included in the output:
<?xml version="1.0" encoding="UTF-8"?>
<countries>
<Name language="en">Australia</Name>
</countries>

Related

How can I get the attribute value?(javax.xml.bind.annotation)

Im using javax.xml.bind.annotation and I need to get the attribute "xmlns:language" (see below xml)
<type xmlns:language="ru" xmlns:type="string">Some text</type>
What annotation should I use?
#XmlRootElement(name = "type")
#XmlAccessorType(XmlAccessType.FIELD)
public class Type {
#XmlValue
protected String value;
#XmlAttribute
protected String language;
}
In the document from your question you are declaring that the prefix language will be associated with the namespace ru.
<type xmlns:language="ru" xmlns:type="string">Some text</type>
I do not believe the above is what you are trying to do. If you are trying to specify the language for your document I would recommend using the xml:lang attribute instead (as suggested by Ian Roberts).
<type xml:lang="ru">Some text</type>
Type
Then you would map to it as follows using the #XmlAttribute annotation:
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Type {
#XmlValue
protected String value;
#XmlAttribute(name = "lang", namespace = "http://www.w3.org/XML/1998/namespace")
protected String language;
}
Demo
import java.io.StringReader;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Type.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
StringReader xml = new StringReader(
"<type xml:lang='ru' xmlns:type='string'>Some text</type>");
Type type = (Type) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(type, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<type xml:lang="ru">Some text</type>

Java Code to XML/XSD without using Annotation

I need to marshall and unmarshall a Java class to XML. The class in not owned by me, that I cannot add anotations so that I can use JAXB.
Is there a good way to convert the Java to XML with the given contraint?
Also, thought a tool may be helpful, but I would be more intersted it there is some Java API to do the same.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
DOMAIN MODEL
I will use the following domain model for this answer. Note how there are no JAXB annotations on the model.
Customer
package forum11693552;
import java.util.*;
public class Customer {
private String firstName;
private String lastName;
private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>();
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public List<PhoneNumber> getPhoneNumbers() {
return phoneNumbers;
}
public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
this.phoneNumbers = phoneNumbers;
}
}
PhoneNumber
package forum11693552;
public class PhoneNumber {
private String type;
private String number;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
OPTION #1 - Any JAXB (JSR-222) Implementation
JAXB is configurartion by exception, this means you only need to add annotations where you want the mapping behaviour to differ from the default. Below is a link to an example demonstrating how to use any JAXB impl without annotations:
Demo
package forum11693552;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");
PhoneNumber workPhone = new PhoneNumber();
workPhone.setType("work");
workPhone.setNumber("555-1111");
customer.getPhoneNumbers().add(workPhone);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer);
marshaller.marshal(rootElement, System.out);
}
}
Output
<customer>
<firstName>Jane</firstName>
<lastName>Doe</lastName>
<phoneNumbers>
<number>555-1111</number>
<type>work</type>
</phoneNumbers>
</customer>
For More Information
http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/TheBasics
OPTION #2 - EclipseLink JAXB (MOXy)'s External Mapping Document
If you do want to customize the mappings, then you may be interested in MOXy's external mapping document extension. A sample mapping document looks like the following:
oxm.xml
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum11693552">
<java-types>
<java-type name="Customer">
<xml-root-element />
<java-attributes>
<xml-element java-attribute="firstName" name="first-name" />
<xml-element java-attribute="lastName" name="last-name" />
<xml-element java-attribute="phoneNumbers" name="phone-number" />
</java-attributes>
</java-type>
<java-type name="PhoneNumber">
<java-attributes>
<xml-attribute java-attribute="type" />
<xml-value java-attribute="number" />
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
jaxb.properties
To enable MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model 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
When using EclipseLink MOXy as your JAXB provider (see), you can leverage the external mapping document when you bootstrap your JAXBContext
package forum11693552;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.namespace.QName;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String,Object>(1);
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11693552/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
Customer customer = new Customer();
customer.setFirstName("Jane");
customer.setLastName("Doe");
PhoneNumber workPhone = new PhoneNumber();
workPhone.setType("work");
workPhone.setNumber("555-1111");
customer.getPhoneNumbers().add(workPhone);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer);
marshaller.marshal(rootElement, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<first-name>Jane</first-name>
<last-name>Doe</last-name>
<phone-number type="work">555-1111</phone-number>
</customer>
For More Information
http://blog.bdoughan.com/2010/12/extending-jaxb-representing-annotations.html
http://blog.bdoughan.com/2012/04/extending-jaxb-representing-metadata-as.html
Have you looked at XStream ? It will deserialise/deserialise a standard POJO without annotations or XSDs. You can provide customisations to affect how elements appear in the XML and pretty much works out-of-the-box.
You could write a custom XmlAdapter and annotate fields of the constrained type with a XmlJavaTypeAdapter annotation. The basics would be something like this:
public enum CannotBeAnnotated { value1, value2; }
#XmlRootElement(name="client")
public class ClientClass {
#XmlJavaTypeAdapter(Bridge.class)
public CannotBeAnnotated;
}
#XmlRootElement(name="representation")
public class XmlType {
#XmlValue
public String value;
}
public class Bridge extends XmlAdapter<XmlType, CannotBeAnnotated>{
public XmlType marshal(CannotBeAnnotated c) {
XmlType x=new XmlType();
x.value=c.name();
return x;
}
public CannotBeAnnotated unmarshall(XmlType x) {
return CannotBeAnnotated.valueOf(x.value);
}
}
Of course for enums this would not be useful as JAXB knows how to deal with them. I just picked an enum for simplicity so you can see the idea:
Design an XML representation that you do control
Write an adapter converting that Java type into the desired type
Annotate "client" code referencing the adapter for the desired type
Profit.

Unmarshalling XML using JAXB

I went through almost all questions related to this topic here. But was not able to get a proper solution.
My issue is as follows:
I created a simple program to unmarshall an xml file for which i had a xsd. I was able to do that successfully. But if i am getting an xml without xsd, how can I get my attributes from that, if the xml looks something like this :
<items>
<item>
<code>12000</code>
<name>Samsung 620</name>
<price>9999</price>
</item>
<item>
<code>15000</code>
<name>NOKIA</name>
<price>19999</price>
</item>
<item>
<code>18000</code>
<name>HTC 620</name>
<price>29999</price>
</item>
</items>
Here I don't have an xsd to generate my classes. How can i proceed? Kindly help me.
Thank You
Below is one way that you could map your use case with a JAXB (JSR-222) implementation:
Items
We will use the following class for the root object and annotate it with #XmlRootElement. The #XmlRootElement annotation tells JAXB that this class should be instantiated if the root element in the document being unmarshalled is items, you can also specify a different name #XmlRootElement(name="foo").
package forum11152046;
import java.util.List;
import javax.xml.bind.annotation.*;
#XmlRootElement
public class Items {
private List<Item> items;
#XmlElement(name="item")
public List<Item> getItems() {
return items;
}
public void setItems(List<Item> items) {
this.items = items;
}
}
Item
In this example I created a class where all the property names correspond directly to the names in the XML document. This means there aren't any annotations that need to be added. If you need to override the default name you can use an annotation such as #XmlElement to do so. I used the #XmlElement annotation to do this in the Items class for the items property.
package forum11152046;
public class Item {
private int code;
private String name;
private int price;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
}
Demo
package forum11152046;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Items.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum11152046/input.xml");
Items items = (Items) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(items, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<items>
<item>
<code>12000</code>
<name>Samsung 620</name>
<price>9999</price>
</item>
<item>
<code>15000</code>
<name>NOKIA</name>
<price>19999</price>
</item>
<item>
<code>18000</code>
<name>HTC 620</name>
<price>29999</price>
</item>
</items>
If you want to stick with JAXB, you can either write an XML Schema Document on your own to validate such XML (it looks simple but it's just an instance, you need to find out what could change in these documente beforehand) or create a POJO with JAXB annotations matching these nodes. I'm afraid there's no other way. You still have to know well what the format allows.

error while unmarshalling "Type mismatch: cannot convert from XmlAccessType to AccessType "

I am working with Jaxb, Unmarshalling an xml. I am using java 1.6. This is the class which is generated through JWSDP 2.0. (xjc.bat) but what my problem is i am unable to compile the generated class. i am getting an syntax error as shown below.
"Type mismatch: cannot convert from XmlAccessType to AccessType"
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)// here i am getting sytax error
#XmlType(name = "personinfo", propOrder = {
"firstname",
"lastname",
"address"
})
public class Personinfo {
#XmlElement(required = true)
protected String firstname;
#XmlElement(required = true)
protected String lastname;
#XmlElement(name = "Address", required = true)
protected PersonAddress address;
............................
can any one help in this regard,
I tried the Personinfo class from your question with the demo code below and everything worked correctly. Since you are using Java SE 6 (which includes a JAXB implementation) you will want to make sure you don't have any of the JAXB APIs from the JWSDP 2.0 on your class path.
I would also recommend using the XJC utility from Java SE 6 instead of JWSDP, as JWSDP is quite old:
http://java.sun.com/webservices/downloads/previous/webservicespack.jsp
Demo
package forum10514244;
import java.io.File;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Personinfo.class);
File xml = new File("src/forum10514244/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Personinfo> je = unmarshaller.unmarshal(new StreamSource(xml), Personinfo.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(je, System.out);
}
}
input.xml/Output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<firstname>Jane</firstname>
<lastname>Doe</lastname>
<Address/>
</root>

DescriptorException "Missing class indicator field from database row" exception when unmarshalling XML using EclipseLink JAXB (MOXy)

I'm having a problem using Moxy to marshal/unmarshal Salesforce outbound message XMLs. The exception that I'm getting is:
javax.xml.bind.JAXBException
- with linked exception:
[Exception [EclipseLink-44] (Eclipse Persistence Services - 2.2.0.v20110202-r8913): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: Missing class indicator field from database row [UnmarshalRecord()].
Descriptor: XMLDescriptor(service.salesforce.model.SObject --> [])]
The classes that I have are:
#XmlSeeAlso({Account.class, Opportunity.class})
#XmlDiscriminatorNode("#type")
public abstract class SObject {
public abstract String getId();
public abstract void setId(String id);
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlDiscriminatorValue("sf:Opportunity")
public class Opportunity extends SObject {
#XmlElement(name = "Id", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String id;
#XmlElement(name = "AccountId", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String accountId;
#XmlElement(name = "StageName", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String stageName;
#XmlElement(name = "Agreement_Date_Signed__c", namespace = "urn:sobject.enterprise.soap.sforce.com")
private Date agreementDateSigned;
#XmlElement(name = "Agreement_Date_Start__c", namespace = "urn:sobject.enterprise.soap.sforce.com")
private Date agreementDateStart;
#XmlElement(name = "Payment_Method__c", namespace = "urn:sobject.enterprise.soap.sforce.com")
private String paymentMethod;
// getters and setters...
}
The XML that I'm trying to unmarshal is:
<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:out="http://soap.sforce.com/2005/09/outbound" xmlns:urn="urn:sobject.enterprise.soap.sforce.com">
<soapenv:Header/>
<soapenv:Body>
<out:notifications>
<out:OrganizationId>123456789123456789</out:OrganizationId>
<out:ActionId>123456789123456789</out:ActionId>
<out:SessionId>sessId</out:SessionId>
<out:EnterpriseUrl>http://www.enterpriseexample.com</out:EnterpriseUrl>
<out:PartnerUrl>http://www.partnerexample.com</out:PartnerUrl>
<out:Notification>
<out:Id>987654321987654321</out:Id>
<out:sObject xsi:type="sf:Opportunity" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<urn:Id>121212121212121212</urn:Id>
<urn:AccountId>121212121212121212</urn:AccountId>
<urn:Agreement_Date_Signed__c>2012-01-01T08:34:56Z</urn:Agreement_Date_Signed__c>
<urn:Agreement_Date_Start__c>2012-02-01</urn:Agreement_Date_Start__c>
<urn:StageName>Booking</urn:StageName>
</out:sObject>
</out:Notification>
</out:notifications>
</soapenv:Body>
</soapenv:Envelope>
Any idea?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB 2 (JSR-222) expert group.
Since you are using the xsi:type attribute as your discriminator node as your inheritance indicator, I would recommend using the standard JAXB mechanisms rather than MOXy's #XmlDescriminatorNode/#XmlDescriminatorValue extension.
SObject
package forum987537;
import javax.xml.bind.annotation.XmlSeeAlso;
#XmlSeeAlso({Account.class, Opportunity.class})
public abstract class SObject {
public abstract String getId();
public abstract void setId(String id);
}
Opportunity
package forum987537;
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name="Opportunity")
public class Opportunity extends SObject {
private String id;
#Override
public String getId() {
return id;
}
#Override
public void setId(String id) {
this.id = id;
}
}
Notification
package forum987537;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="Notification", namespace="http://soap.sforce.com/2005/09/outbound")
public class Notification {
private SObject sObject;
#XmlElement(namespace="http://soap.sforce.com/2005/09/outbound")
public SObject getsObject() {
return sObject;
}
public void setsObject(SObject sObject) {
this.sObject = sObject;
}
}
package-info
#XmlSchema(
namespace="urn:sobject.enterprise.soap.sforce.com",
elementFormDefault=XmlNsForm.QUALIFIED,
xmlns={
#XmlNs(prefix="out", namespaceURI = "http://soap.sforce.com/2005/09/outbound"),
#XmlNs(prefix="sf", namespaceURI = "urn:sobject.enterprise.soap.sforce.com"),
})
package forum987537;
import javax.xml.bind.annotation.*;
Demo
package forum987537;
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(Notification.class);
Notification notification = new Notification();
Opportunity opportunity = new Opportunity();
opportunity.setId("ABC123");
notification.setsObject(opportunity);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(notification, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<out:Notification xmlns:sf="urn:sobject.enterprise.soap.sforce.com" xmlns:out="http://soap.sforce.com/2005/09/outbound">
<out:sObject xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="sf:Opportunity">
<sf:id>ABC123</sf:id>
</out:sObject>
</out:Notification>
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-xsitype.html
http://blog.bdoughan.com/2012/02/jaxb-and-inheritance-eclipselink-moxy.html

Categories