JAXB Unmarshall to List - java

I am trying to unmarshall this XML to Java objects, a Customer object containing a List of EmailAdresses.
<customer>
<emailAddresses>janed#example.com</emailAddresses>
<emailAddresses>jdoe#example.org</emailAddresses>
</customer>
Having an issue with the list, I get the correct number of list items (2), but the value of the emailAddresses tag is null
Customer.java
#XmlRootElement( name = "customer" )
public class Customer
{
private List<EmailAddress> emailAddresses;
public Customer()
{
emailAddresses = new ArrayList<EmailAddress>();
}
public List<EmailAddress> getEmailAddresses()
{
return emailAddresses;
}
public void setEmailAddresses( List<EmailAddress> emailAddresses )
{
this.emailAddresses = emailAddresses;
}
}
EmailAddress.java
public class EmailAddress
{
private String emailAddresses;
public String getEmailAddresses()
{
return emailAddresses;
}
public void setEmailAddresses( String emailAddresses )
{
this.emailAddresses = emailAddresses;
}
}
Failing Unit Test
#Test
public void shouldDeserialiseCusomerXMLToObject() throws JAXBException
{
String xml = "<customer>"
+ " <emailAddresses>janed#example.com</emailAddresses>"
+ " <emailAddresses>jdoe#example.org</emailAddresses>"
+ "</customer>";
JAXBContext jaxbContext = JAXBContext.newInstance( Customer.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
StringReader reader = new StringReader( xml );
Customer msg = ( Customer ) jaxbUnmarshaller.unmarshal( reader );
// This passes, I have 2 emailAddresses
assertEquals( 2, msg.getEmailAddresses().size() );
// This fails, I have a null pointer instead of the email address
assertEquals( "janed#example.com", msg.getEmailAddresses().get( 0 ).getEmailAddresses() );
}

The emailAddresses field of EmailAddress is by default treated as a subelement, expecting the XML to be:
<customer>
<emailAddresses>
<emailAddresses>janed#example.com</emailAddresses>
</emailAddresses>
<emailAddresses>
<emailAddresses>jdoe#example.org</emailAddresses>
</emailAddresses>
</customer>
Since your outer <emailAddresses> element doesn't contain an inner <emailAddresses> element, the field is never assigned.
You want the emailAddresses field of EmailAddress to be the value of the (outer) <emailAddresses> element, so you have to tell JAXB that, by specifying the #XmlValue annotation:
#XmlValue
public String getEmailAddresses()
{
return emailAddresses;
}
The #XmlValue annotation is especially useful when combined with #XmlAttribute, to support XML like this:
<Person sex="male" age="25">John Doe</Person>
Where class would then be:
public class Person {
public enum Sex {
#XmlEnumValue("male") MALE,
#XmlEnumValue("female") FEMALE,
}
#XmlAttribute
private Sex sex;
#XmlAttribute
private int age;
#Value
private String name;
}

You have a level too many of email addresses.
If you should change the list of email addresses to a list of strings, like
#XmlRootElement( name = "customer" )
public class Customer
{
private List<String> emailAddresses;
public Customer()
{
emailAddresses = new ArrayList<String>();
}
public List<String> getEmailAddresses()
{
return emailAddresses;
}
public void setEmailAddresses( List<String> emailAddresses )
{
this.emailAddresses = emailAddresses;
}
}

Related

Parameterizing strings in a model not working

I'm trying to parameterize a string and set that string as the result of a model:
SomePanel.java
public SomePanel( String id, IModel<Person> personModel)
{
tallLabel = new Label( "height", new LoadableDetachableModel() {
pubic String load() {
Person person = personModel.getObject();
boolean isTall = apiCallToCheckIfTall( person );
// 'name' is a property on PersonModel
String name = person.getName();
String tallString = MessageFormat.format(getString("Tall.Label"), name );
String shortString = MessageFormat.format(getString("Short.Label"), name );
return isTall ? tallString : shortString;
}
});
add(tallLabel);
}
Text.properties
Tall.Label = ${name} is tall.
Short.Label = ${name} is short.
I tried implementing a solution but contact.getName() produces an error. My understanding is that personModel.getObject() would give me the actual object (which has getter getName defined) so not sure why this would produce an error.
MessageFormat uses indexed parameters, so you probably mixed up some technologies here.
Here's the simplest solution using Wicket's resource messages with names parameters:
return getString(isTall ? "Tall.Label" : "Short.Label", personModel)
I managed to get it to work with:
SomePanel.java
public SomePanel( String id, IModel<Person> personModel)
{
tallLabel = new Label( "height", new LoadableDetachableModel() {
public String load() {
Person person = personModel.getObject();
boolean isTall = apiCallToCheckIfTall( person );
PersonGetter getter = new PersonGetter ( personModel );
String name = getter.getName();
String RTStringModel = MessageFormat.format( getString("Tall.Label"), person.getName() );
String StringModel = MessageFormat.format( getString("Short.Label"), person.getName() );
return isTall ? RTStringModel : StringModel;
}
});
add(tallLabel);
}
...
private class NameGetter implements Serializable
{
private final IModel<Person> model;
public NameGetter( final IModel<Person> personModel )
{
this.model = person;
}
public String getName()
{
return getFormattedLegalName( this.model.getObject() );
}
}
public static final String getFormattedLegalName( Person person )
{
if ( person == null )
{
return "";
}
else
{
return person.getName();
}
}
Text.properties
Tall.Label = {0} is tall.
Short.Label = {0} is short.
This seems to be a bit too much to extract a value from the model though. I couldn't get the name from the personModel directly (e.g. personModel.getObject().getName()) and went the convoluted route of having to create another class.

Java UnMarshall XML gives null objects

UnMarshalling XML gives null java objects.
XML :
<?xml version="1.0" encoding="UTF-8"?>
<Items>
<Item Name="John"/>
<Item Name="Jason"/>
</Items>
Items class :
#XmlRootElement(name = "Items")
#XmlAccessorType(XmlAccessType.FIELD)
public class Items{
#XmlElement
private List<Item> item;
public List<Item> getItem() {
return this.Item;
}
}
Item class :
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "Item")
public class Item{
#XmlAttribute
private String name;
public String getName() {
return this.Name;
}
}
Java Code that UnMarshalls : Here result.getBody gives XML String
ResponseEntity<String> result = restTemplate.exchange(uri, HttpMethod.GET, entity, String.class);
JAXBContext jaxbContext = JAXBContext.newInstance(Items.class);
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Items itResult = (Items) jaxbUnmarshaller.unmarshal(new StringReader(result.getBody()));
Item object always comes null. How to unmarshall the xml correctly ?
Thanks in Advance. :
Use the following classes :
Item.java
#XmlRootElement(name = "Item")
public class Item
{
private String name;
public String getName()
{
return this.name;
}
#XmlAttribute(name = "Name" )
public void setName( String name )
{
this.name = name;
}
}
Items.java
#XmlRootElement(name = "Items")
public class Items
{
#XmlElementWrapper( name = "Items")
private List<Item> items = new ArrayList<Item>();
public List<Item> getItemList()
{
return this.items;
}
#XmlElement(name = "Item")
public void setItemList( List<Item> items )
{
this.items = items;
}
}
Test.java
public class Test
{
public static void main( String... args )
{
try
{
JAXBContext jaxbContext = JAXBContext.newInstance( Items.class );
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Items itResult = (Items) jaxbUnmarshaller.unmarshal( new File( "Items.xml" ) );
if ( itResult != null )
{
List<Item> items = itResult.getItemList();
for ( Item item : items )
{
System.out.println( item.getName() );
}
}
}
catch ( Exception e )
{
e.printStackTrace();
}
}
}
You should get the Items object which contains Item s.
Changes I made:
a) You need an #XmlElementWrapper on the list which says that the Items is a wrapper around Items.
b) Move the #XmlAttribute to setter in Item.java
c) Move the #XmlElement to setter in Items.java
Item Class:
public class Item {
private String Name;
}
Items Class:
#XmlRootElement(name = "Items")
#XmlAccessorType(XmlAccessType.FIELD)
public class Items {
#XmlElement(name = "Item")
private List<Item> items;
}
Unmarshall class:
JAXBContext jaxbContext;
Unmarshaller jaxbUnmarshaller;
try {
jaxbContext = JAXBContext.newInstance(Items.class);
jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Items itResult = (Items) jaxbUnmarshaller.unmarshal(file);
System.out.println(itResult);
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
You missed some (name = "...") in your JAXB annotations,
and therefore you got some default names not matching your XML contents.
And that's why these fields remained null when unmarshalling your XML.
Items.java
For field item you need to explicitly set (name = "Item").
Otherwise you would get the default name "item", which is wrong.
#XmlRootElement(name = "Items")
#XmlAccessorType(XmlAccessType.FIELD)
public class Items {
#XmlElement(name = "Item")
private List<Item> item;
// ... getter
}
Item.java
For field name you need to explicitly set (name = "Name").
Otherwise you would get the default name "name", which is wrong.
And by the way, you don't need #XmlRootElement here,
because it has no effect on non-root elements.
#XmlAccessorType(XmlAccessType.FIELD)
public class Item {
#XmlAttribute(name = "Name")
private String name;
// ... getter
}

SimpleFramwork XML: Element with Inner Text and Child Elements

I have the following situtation in deserializing xml using SimpleFramework of specific format that cannot be changed...
<Question ID="Q1">
THIS INNER TEXT IS THE ISSUE
<Criteria Type="Normal" Source="OEM">
<Value Type="0">45.7</Value>
<Value Type="100">42.7</Value>
</Criteria>
<Criteria Type="Impact" Source="OEM">
<Value Type="0">45.7</Value>
<Value Type="100">42.7</Value>
</Criteria>
<!-- CRITERIA CAN HAVE ANY NUMBER -->
</Question>
and here is the class I wrote for Question
#Root (name="Question")
public class Question {
#Attribute (name="ID")
private String id;
#ElementList (inline=true, required=false)
private List<Criteria> criteria;
#Text
private String text;
// And their getter and setters...
}
Now the issue is that, I CANNOT GET INNER TEXT...
Can anybody suggest me the way to do this...???
You can't use #Text annotation here, this is only possible if you don't have any child.
and it [#Text annotation] can not appear with the another XML element annotations, such
as the Element annotation.
Source: #Text API documentation
However, you can use a Converter to those text. This is a bit tricky, but here's an example:
Criteria class:
#Root(name = "Criteria")
public class Criteria
{
#Attribute(name = "Type")
private String type;
#Attribute(name = "Source")
private String source;
#ElementList(name = "Values", inline = true)
private ArrayList<Value> values;
public Criteria(String type, String source)
{
this.type = type;
this.source = source;
this.values = new ArrayList<>();
}
private Criteria() { }
// ...
#Override
public String toString()
{
return "Criteria{" + "type=" + type + ", source=" + source + ", values=" + values + '}';
}
// Inner class for values - you also can use a normal one instead
#Root(name = "Value")
public static class Value
{
#Attribute(name = "Type", required = true)
private int type;
#Text(required = true)
private double value;
public Value(int type, double value)
{
this.type = type;
this.value = value;
}
private Value() { }
}
}
Question class:
#Root(name = "Question")
#Convert( value = Question.QuestionConvert.class)
public class Question
{
#Attribute(name = "ID", required = true)
private String id;
#Element(name = "text")
private String text;
#ElementList(inline = true)
private ArrayList<Criteria> criteria;
public Question(String id)
{
this.id = id;
this.criteria = new ArrayList<>();
this.text = "This inner text ...";
}
private Question() { }
// ...
#Override
public String toString()
{
return "Question{" + "id=" + id + ", text=" + text + ", criteria=" + criteria + '}';
}
static class QuestionConvert implements Converter<Question>
{
private final Serializer ser = new Persister();
#Override
public Question read(InputNode node) throws Exception
{
Question q = new Question();
q.id = node.getAttribute("ID").getValue();
q.text = node.getValue();
q.criteria = new ArrayList<>();
InputNode criteria = node.getNext("Criteria");
while( criteria != null )
{
q.criteria.add(ser.read(Criteria.class, criteria));
criteria = node.getNext("Criteria");
}
return q;
}
#Override
public void write(OutputNode node, Question value) throws Exception
{
node.setAttribute("ID", value.id);
node.setValue(value.text);
for( Criteria c : value.getCriteria() )
{
ser.write(c, node);
}
}
}
}
Please note all those empty constructors. They are required by simple but you can keep them private. You don't have to implement those inner classes as inner.
The key to the solution is the Converter which allows you to use text and child-elements together. You can use a Serializer to write all the Criteria-childs.
There are some toString() methods, those are only for testing - you can implement them as you need.
Input XML:
<Question ID="Q1">This inner text ...
<Criteria Type="Normal" Source="OEM">
<Value Type="0">45.7</Value>
<Value Type="100">42.7</Value>
</Criteria>
<Criteria Type="Impact" Source="OEM">
<Value Type="0">45.7</Value>
<Value Type="100">42.7</Value>
</Criteria>
</Question>
Example code:
Serializer ser = new Persister(new AnnotationStrategy()); // Don't miss the AnnotationStrategy!
Question q = ser.read(Question.class, f);
System.out.println(q);
Output:
Question{id=Q1, text=This inner text ...
, criteria=[Criteria{type=Normal, source=OEM, values=[Value{type=0, value=45.7}, Value{type=100, value=42.7}]}, Criteria{type=Impact, source=OEM, values=[Value{type=0, value=45.7}, Value{type=100, value=42.7}]}]}
Not very beautiful, but it's working! :-)
Ps. Since both methods of the Converter are implemented you also can use this code to serialize a Question object.

Custom IDResolver gets wrong target type

I wrote this custom IDResolver:
final class MyIDResolver extends IDResolver {
Map<String, Course> courses = new HashMap<>();
/**
* #see com.sun.xml.bind.IDResolver#bind(java.lang.String, java.lang.Object)
*/
#Override
public void bind(String id, Object obj) throws SAXException {
if (obj instanceof Course)
courses.put(id, (Course)obj);
else
throw new SAXException("This " + obj.toString() + " cannot found");
}
/**
* #see com.sun.xml.bind.IDResolver#resolve(java.lang.String, java.lang.Class)
*/
#Override
public Callable<?> resolve(final String id, final Class targetType) throws SAXException {
return new Callable<Object>() {
#Override
public Object call() throws Exception {
if (targetType == Course.class)
return courses.get(id);
else
throw new ClassNotFoundException(targetType.toString() + " cannot found");
}
};
}
}
And I have two classes like:
#XmlRootElement(name = "course")
public class Course {
#XmlID
#XmlAttribute
private String id;
#XmlAttribute
private int units;
#XmlAttribute
private Level level;
#XmlAttribute
private String name;
#XmlIDREF
#XmlElement(name = "pre")
private ArrayList<Course> prerequisite;
#XmlIDREF
#XmlElement(name = "co")
private ArrayList<Course> corequisite;
}
#XmlRootElement(name = "dept")
#XmlAccessorType(XmlAccessType.FIELD)
public final class Department {
#XmlAttribute
private String name;
#XmlElementWrapper(name = "courses")
#XmlElement(name = "course")
private ArrayList<Course> courses;
}
And sample XML like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<dept name="ece">
<courses>
<course id="1" units="4" level="UNDERGRADUATE" name="Fundamentals of Programming"/>
<course id="2" units="3" level="UNDERGRADUATE" name="Advanced Programming">
<pre>1</pre>
</course>
</courses>
</dept>
And a simple main like this:
unmarshaller.unmarshal(new FileReader(arg0))
context = JAXBContext.newInstance(Department.class);
unmarshaller = context.createUnmarshaller();
unmarshaller.setProperty(IDResolver.class.getName(), new MyIDResolver());
The resolve() method of a custom IDResolver gets Object.class as targetType. But it should be Course.class. This results in wrong ID resolving. Where is my problem?
You should not use IDResolve when Department contains course. It should be used when Department want to reference to a Course. So for this reason when jaxb arrives a course inside of a department that it contains an element (here <pre>) it will pass object.class to your resolve function.

Access attribute of internal element in the most simple way

Is there any way to do mapping with single java bean for such simple xml:
<item lang="en">
<item-url>some url</item-url>
<parent id="id_123"/>
</item>
I've tried something like this:
#XmlRootElement( name = "item" )
public class Item {
#XmlElement( name = "item-url" )
private String url;
#XmlAttribute( name = "parent/#id" )
// Of course XPath doesn't work here, but it would be great...
private String parentId;
}
In other words - how can I access attribute of internal element without creating of corresponding bean?
You could use an XmlAdapter:
ParentIdAdapter
public class ParentIdAdapter extends XmlAdapter<ParentIdAdapter.AdaptedParentId, String> {
public String unmarshal(AdaptedParentId value) {
return value.id;
}
public AdaptedParentId marshal(String value) {
AdaptedParentId adapted = new AdaptedParentId();
adapted.id = value;
return adapted;
}
public static class AdaptedParentId {
#XmlAttribute
public String id;
}
}
Item
#XmlRootElement( name = "item" )
public class Item {
#XmlElement( name = "item-url" )
private String url;
#XmlElement( name = "parent" )
#XmlJavaTypeAdapter(ParentIdAdapter.class)
private String parentId;
}
If you are using EclipseLink MOXy as your JAXB provider then you could leverage the #XmlPath extension to do the following:
#XmlRootElement( name = "item" )
public class Item {
#XmlElement( name = "item-url" )
private String url;
#XmlPath("parent/#id")
private String parentId;
}
As I didn't want to create redundant classes in my package, the best solution I've found is:
#XmlRootElement( name = "item" )
public class Item {
#XmlRootElement( name = "parent" )
private static class ParentIdWrapper {
#XmlAttribute( name = "id" )
public String id;
}
#XmlElement( name = "item-url" )
private String url;
#XmlElement( name = "parent" )
private ParentIdWrapper parentIdWrap;
public String getParentId() {
return this.parentIdWrap.id;
}
}

Categories