I have the follwoing xml element:
<FIELD1><COMP VAR="A">text B</COMP> inner text <COMP VAR="B">text B</COMP></FIELD1>
How to annotate this property with JAXB:
protected List<Object> compOrValue;
to have a list of COMP xml elemnts and String values.
Is it possible with JAXB?
Thanks
You can use a combination of #XmlAnyElement and #XmlMixed to achieve this:
import java.util.List;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlMixed;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="FIELD1")
public class Root {
protected List<Object> compOrValue;
#XmlAnyElement
#XmlMixed
public List<Object> getCompOrValue() {
return compOrValue;
}
public void setCompOrValue(List<Object> compOrValue) {
this.compOrValue = compOrValue;
}
}
Related
How can I convert a String into a Map:
Map m = convert("A=4 H=X PO=87"); // What's convert?
System.err.println(m.getClass().getSimpleName()+m);
Expected output
HashMap{A=4, H=X, PO=87}
There is no need to reinvent the wheel. The Google Guava library provides the Splitter class.
Here's how you can use it along with some test code:
package com.sandbox;
import com.google.common.base.Splitter;
import org.junit.Test;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class SandboxTest {
#Test
public void testQuestionInput() {
Map<String, String> map = splitToMap("A=4 H=X PO=87");
assertEquals("4", map.get("A"));
assertEquals("X", map.get("H"));
assertEquals("87", map.get("PO"));
}
private Map<String, String> splitToMap(String in) {
return Splitter.on(" ").withKeyValueSeparator("=").split(in);
}
}
package com.sandbox;
import com.google.common.base.Splitter;
import org.junit.Test;
import java.util.Map;
import static org.junit.Assert.assertEquals;
public class SandboxTest {
#Test
public void testQuestionInput() {
Map<String, String> map = splitToMap("A=4 H=X PO=87");
assertEquals("4", map.get("A"));
assertEquals("X", map.get("H"));
assertEquals("87", map.get("PO"));
}
private Map<String, String> splitToMap(String in) {
return Splitter.on(" ").withKeyValueSeparator("=").split(in);
}
}
I am trying to simplify the conversion of POJO to XML, but I am facing issues with Class field attributes.
Consider the following XML files
<cat>
<displayTexts>
<displayText language="en">12</displayText>
<displayText language="en">23</displayText>
</displayTexts>
</cat>
And
I have a class
#XmlRootElement(name = "cat")
public class Category{
List<Integer> list;
public List<Integer> getList() {
return list;
}
#XmlElementWrapper(name = "displayTexts")
#XmlElement(name = "displayText")
public void setList(List<Integer> list) {
this.list = list;
}
}
How can I write an adaptor which will produce XML mentioned as above?
Right now it will produce something like below xml ::
<cat>
<displayTexts>
<displayText>12</displayText>
<displayText>23</displayText>
</displayTexts>
</cat>
Note:: I am not allowed to use MoXy. And I know I can achieve this by writing a different class. The question is can we write an adaptor so that I can generalize this attribute thing for any class field?
Although I would still recommend, using a class Instead of Integer to use the value for displayText and it's attribute, but still if you want to achieve without any new class, you can give following a shot using #XmlAnyElement for your displayList and "creating" the xml yourself:
import java.util.Arrays;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
#XmlRootElement(name = "cat")
public class Category {
List<Integer> list;
#XmlAnyElement()
#XmlJavaTypeAdapter(MyIntegerAdapter.class)
public List<Integer> getList() {
return list;
}
//#XmlElement(name = "displayText")
public void setList(List<Integer> list) {
this.list = list;
}
public static void main(String[] args) throws Exception {
Category bx = new Category();
List<Integer> lists = Arrays.asList(121, 212);
bx.setList(lists);
JAXBContext jc = JAXBContext.newInstance(Category.class);
MyIntegerAdapter adapter = new MyIntegerAdapter();
Marshaller marrshaller = jc.createMarshaller();
marrshaller.setAdapter(adapter);
marrshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marrshaller.marshal(bx, System.out);
}
}
class MyIntegerAdapter extends XmlAdapter<Element, List<Integer>> {
private DocumentBuilder documentBuilder;
public MyIntegerAdapter() {
}
private DocumentBuilder getDocumentBuilder() throws Exception {
// Lazy load the DocumentBuilder as it is not used for unmarshalling.
if (null == documentBuilder) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
documentBuilder = dbf.newDocumentBuilder();
}
return documentBuilder;
}
#Override
public Element marshal(List<Integer> lists) throws Exception {
if (null == lists) {
return null;
}
Document document = getDocumentBuilder().newDocument();
Element element = document.createElement("displayTexts");
for (int value : lists) {
Element displayText = document.createElement("displayText");
System.out.println("value.." + value);
displayText.setTextContent(value + "");
displayText.setAttribute("Lang", "en");
element.appendChild(displayText);
}
return element;
}
#Override
public List<Integer> unmarshal(Element v) throws Exception {
//TODO
throw new UnsupportedOperationException();
}
}
I have a class Person with attributes name and address. I display it in a XML. While unmarshalling from XML will it be possible to get line number for name and address separately.
I tried using Locator. But it does not provide individual line numbers.
The EclipseLink JAXB (MOXy) and the JAXB reference implementation each have their own #XmlLocation annotations for supporting this use case. This allows you to store the location on the XML element corresponding to the object as an instance of org.xml.sax.Locator. Since I'm the MOXy lead, I will demonstrate using MOXy:
Person
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
#XmlJavaTypeAdapter(value=StringAdapter.class)
String name;
Address address;
#XmlLocation
Locator locator;
}
Address
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
public class Address {
#XmlJavaTypeAdapter(value=StringAdapter.class)
private String street;
#XmlLocation
Locator locator;
}
StringAdapter
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlLocation;
import org.xml.sax.Locator;
public class StringAdapter extends XmlAdapter<StringAdapter.AdaptedString, String> {
public static class AdaptedString {
#XmlValue
public String value;
#XmlLocation
#XmlTransient
Locator locator;
}
#Override
public String unmarshal(AdaptedString v) throws Exception {
System.out.println(v.value + " " + v.locator.getLineNumber());
return v.value;
}
#Override
public AdaptedString marshal(String v) throws Exception {
AdaptedString adaptedString = new AdaptedString();
adaptedString.value = v;
return adaptedString;
}
}
jaxb.properties
To specify 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.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum14455596/input.xml");
Person person = (Person) unmarshaller.unmarshal(xml);
System.out.println("Person: " + person.locator.getLineNumber());
System.out.println("Address: " + person.address.locator.getLineNumber());
}
}
Output
Jane Doe 3
1 A Street 5
Person: 2
Address: 4
You could leverage a StAX StreamReaderDelegate and do something like the following:
Demo
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.stream.util.StreamReaderDelegate;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Person.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource source = new StreamSource("src/forum14455596/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(source);
xsr = new StreamReaderDelegate(xsr) {
#Override
public String getLocalName() {
String localName = super.getLocalName();
if(isStartElement()) {
System.out.println(localName + " " + this.getLocation().getLineNumber());
}
return localName;
}
};
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.unmarshal(xsr);
}
}
Person
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Person {
private String name;
private String address;
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<person>
<name>Jane Doe</name>
<address>1 A Street</address>
</person>
Output
person 2
name 3
address 4
There is a pseudo code like this:
Alma alma = new Alma();
alma.setKorte(""); //Korte is a string member
marshaller.marshal(alma, stringwriter);
System.out.println(stringwriter.toString());
And it produces the output of (I know this is some kind of trick that the empty element is there, but this is how it works in my system, so someone before me have set this like this):
<alma><korte/></alma>
Which is fine for me. But when I unmarshal it, the empty string is not unmarshalled correctly, but korte will be null. How to make jaxb to unmarshal the empty element into empty string?
I use JDK6 bundled jaxb.
EDIT:
The alma class looks like (name of class is changed, but it is like this):
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "Alma", propOrder = {
"korte"
})
public class Alma
implements Serializable
{
private final static long serialVersionUID = 100L;
#XmlElement(required = true)
protected String korte;
JAXB implementations should unmarshal empty elements as "" for String properties. The solution will be to upgrade to a newer version of your JAXB implementation that contains this fix.
The example below worked for me using the version of JAXB included in JDK 1.6.0_20 and EcliseLink JAXB (MOXy) 2.3.
Demo
import java.io.StringReader;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Alma.class);
String xmlString = "<alma><korte/></alma>";
StringReader xmlReader = new StringReader(xmlString);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Alma alma = (Alma) unmarshaller.unmarshal(xmlReader);
System.out.println(alma.getKorte().length());
}
}
Output
0
Alma
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement
#XmlType(name = "Alma", propOrder = { "korte" })
public class Alma implements Serializable {
private final static long serialVersionUID = 100L;
#XmlElement(required = true)
protected String korte;
public String getKorte() {
return korte;
}
public void setKorte(String korte) {
this.korte = korte;
}
}
I have a XML file, and its xml schema contains couples complexType in it. So when I unmarshal the xml file, I want to give the xml parser my xml schema. Is it possible to do it, if so, then how to do it?
EDIT: After I unmarshal, every field in my object is null. Any idea why?
UPDATE
The issue you are seeing is due to the content being nested within the NameAndAddress element. You could introduce a NameAndAddress class have PackageLabel hold an instance of that.
PackageLabel
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name="PackageLabel")
#XmlAccessorType(XmlAccessType.FIELD)
public class PackageLabel implements Serializable {
#XmlElement(name="NameAndAddress")
private NameAndAddress nameAndAddress;
}
NameAndAddress
import javax.xml.bind.annotation.XmlElement;
public class NameAndAddress {
#XmlElement(name="Name")
private String name;
#XmlElement(name="Address1")
private String address1;
#XmlElement(name="Address2")
private String address2;
#XmlElement(name="City")
private String city;
#XmlElement(name="State")
private String state;
#XmlElement(name="ZipCode")
private String zipCode;
}
EclipseLink JAXB (MOXy)'s #XmlPath Extension
Alternatively you could use the #XmlPath extension in EclipseLink JAXB (MOXy):
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="PackageLabel")
#XmlAccessorType(XmlAccessType.FIELD)
public class PackageLabel implements Serializable {
#XmlPath("NameAndAddress/Name/text()")
private String name;
#XmlPath("NameAndAddress/Address1/text()")
private String address1;
#XmlPath("NameAndAddress/Address2/text()")
private String address2;
#XmlPath("NameAndAddress/City/text()")
private String city;
#XmlPath("NameAndAddress/State/text()")
private String state;
#XmlPath("NameAndAddress/ZipCode/text()")
private String zipCode;
}
For More Information
http://bdoughan.blogspot.com/2010/09/xpath-based-mapping-geocode-example.html
http://bdoughan.blogspot.com/2010/07/xpath-based-mapping.html
You can set an XML schema on an instance of Unmarshaller. This will cause JAXB to validate the input while it converts the XML to objects:
http://bdoughan.blogspot.com/2010/12/jaxb-and-marshalunmarshal-schema.html
If you want to generate an object model from an XML schema, you can also use JAXB to do that:
http://bdoughan.blogspot.com/2010/09/processing-atom-feeds-with-jaxb.html