Access attribute of internal element in the most simple way - java

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;
}
}

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.

JAXB Unmarshall to List

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;
}
}

JAX-RS - toString() of associated object in JSON property value

I have these two Java classes
public class Artist {
#Id
private Integer id;
private String name;
private Short birthYear;
#JsonIgnore // This is Jackson
#OneToMany(mappedBy = "artist", fetch = FetchType.EAGER) // This is JPA
private List<Album> albums = new ArrayList<Album>();
. . .
#Override
public String toString() {
return name;
}
}
public class Album {
#Id
private Integer id;
private String name;
private Short releaseYear;
#ManyToOne // This is JPA
private Artist artist;
}
Now, what I want is when I produce JSON objects of the Album class it returns something like this:
{
id : 2012000587,
name : 'Escultura',
releaseYear : '2012',
artist : 'Guaco'
}
Right now it outputs:
{
id : 2012000587,
name : 'Escultura',
releaseYear : '2012',
artist : {
id : 2044452000,
name : 'Guaco',
birthYear : 1987
}
}
I want to avoid at any cost using custom serializers for this matter.
How can I do that?
Try to add a getter for name property and annotation it with a #JsonValue annotation.
public class Artist {
private String name;
...
#JsonValue
public String getName() { return name; }
}
Here is the link to the Jackson Wiki page for reference.

Correct way to parse XML using Simple Framework

I'm using Simple framework to deserialize an xml on my android app .
The issue is on a portion of the xml because I can get the objects of the other portions of it .
Here is the part of the xml that I struggle with :
<webtv name="channelname" date="2014-10-31">
<config>
<pathVideo extension="mp4">http://vodflash.channelname.com/tv_conn/1/country/</pathVideo>
<pathBigImage extension="jpg">http://vodflash.channelname.com/tv_conn/1/country/pic/320x180/</pathBigImage>
<pathImage extension="jpg">http://vodflash.channelname.com/tv_conn/1/country/pic/160x90/</pathImage>
<pays>GB;IE</pays>
</config>
Here is my XmlMapper class :
#Root(name="webtv", strict = false)
public class XmlModelMapper {
public ConfigObject getConfigObjects() {
return configObject;
}
#Element(name="config")
public ConfigObject configObject = new ConfigObject();
#ElementList(name = "itemList")
private List<Videos> itemList = new ArrayList<Videos>();
public List<Videos> getItemList() {
return itemList;
}
#ElementList(name = "chaineList")
private List<Chaine> chaineList = new ArrayList<Chaine>();
public List<Chaine> getChaineList() {
return chaineList;
}
}
If I change my mapper class to this :
#Root(name="webtv", strict = false)
public class XmlModelMapper {
public List<ConfigObject> getConfigObjects() {
return configObject;
}
//change is here using a list not an object
#ElementList(name="config")
public List<ConfigObject> configObjects = new ArrayList<ConfigObject>();
#ElementList(name = "itemList")
private List<Videos> itemList = new ArrayList<Videos>();
public List<Videos> getItemList() {
return itemList;
}
#ElementList(name = "chaineList")
private List<Chaine> chaineList = new ArrayList<Chaine>();
public List<Chaine> getChaineList() {
return chaineList;
}
}
and the log the size of the List I get 4 which is correct , but how to get each object in a distinct way including the extension (attribute)
Please help me solving this issue .
Thanks
#ElementList(name="config")
public List<ConfigObject> configObjects = new ArrayList<ConfigObject>();
This wont match with your xml listed above. While the #Element-based solution (example 1) will create a proper config-tag, the list adds another "list-tag"; here's an example:
<config> <!-- This is the list's tag, wrapping all elements -->
<config> <!-- This is the actual element's tag -->
<pathVideo extension="mp4">http://video.com</pathVideo>
<pathBigImage extension="jpg">http://bigimg.com</pathBigImage>
<pathImage extension="jpg">http://image.com</pathImage>
<pays>GB;IE</pays>
</config>
<!-- some more <config>...</config> -->
</config>
The solution: Use athe inline list - those don't have the "list-tag".
#ElementList(name = "config", inline = true, entry = "config")
public List<ConfigObject> configObjects = new ArrayList<>();
For the sake of completeness, here are my classes used for testing:
class Chaine { /* empty - enough for testing */ }
class Videos { /* empty - enough for testing */ }
#Root
public class ConfigObject
{
#Element(name = "pathVideo")
private PathConfig video;
#Element(name = "pathBigImage")
private PathConfig bigImage;
#Element(name = "pathImage")
private PathConfig image;
#Element(name = "pays")
private String pays;
// ...
}
#Root
public class PathConfig
{
#Attribute(name = "extension")
private String extension;
#Text
private String path;
// ...
}
Solved my issue and learned alot about simple framework which is great and light weight , but I prefer ollo's answer
Please discard ormlite annotations
My classes :
#Root(name="webtv", strict = false)
public class XmlModelMapper {
public ConfigObject getConfigObject() {
return configObject;
}
#Element(name="config")
public ConfigObject configObject = new ConfigObject();
//rest of the elements
...
}
and
#Root(name = "config" , strict = false)
public class ConfigObject {
#Element
PathVideo pathVideo;
#Element
PathImage pathImage;
#Element
PathBigImage pathBigImage ;
//rest of the elements
}
and
#Root(name = "pathImage")
#DatabaseTable(tableName = "imageconfig")
public class PathImage {
#DatabaseField(generatedId = true)
Integer id ;
#Attribute
#DatabaseField
String extension;
#Text
#DatabaseField
String pathImage;
//rest of the elements...
}
and
#Root(name = "pathVideo")
#DatabaseTable(tableName = "videoconfig")
public class PathVideo {
#DatabaseField(generatedId = true)
Integer id ;
#Attribute
#DatabaseField
String extension;
#Text
#DatabaseField
String pathVideo;
}
and finally
#Root(name = "pathBigImage")
#DatabaseTable(tableName = "bigimageconfig")
public class PathBigImage {
#DatabaseField(generatedId = true)
Integer id ;
#Attribute
#DatabaseField
String extension;
#Text
#DatabaseField
String pathBigImage;
//rest of the elements...
}

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.

Categories