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...
}
Related
I am trying to unmarshall an XML into Java POJO using JAXB, all elements are getting unmarshalled fine except for the lists(listId1 and listId2), following is the xml, the pojo classes and the business class.
<?xml version="1.0" encoding="UTF-8"?>
<abacus-1.0-snapshot xmlns="urn:xxxxxxx">
<rules>
<rule>
<name>rule1</name>
<errorType>WARNING</errorType>
<flag>true</flag>
<startDate>2020-05-19</startDate>
<listId1>
<id>100101</id>
<id>100102</id>
</listId1>
<listId2>
<id>100103</id>
<id>100104</id>
</listId2>
</rule>
<rule>
<name>rule2</name>
<errorType>ERROR</errorType>
<flag>false</flag>
<startDate>2020-05-20</startDate>
<listId1>
<id>100105</id>
<id>100106</id>
</listId1>
<listId2>
<id>100107</id>
<id>100107</id>
</listId2>
</rule>
</rules>
</abacus-1.0-snapshot>
The Rules.java which maps the <rules> element to Rules.java
#XmlAccessorType(XmlAccessType.NONE)
#XmlRootElement(name = "rules", namespace = "urn:xxxxxxx")
public class RulesPOJO
{
#XmlElement(name = "rule", namespace = "urn:xxxxxxx")
private final List<rulePOJO> rulesPOJO = new ArrayList<rulePOJO>();
public List<rulePOJO> getRulesPOJO()
{
return rulesPOJO;
}
}
The Rule.java class which is used to map the <rule> element to Rule.java
#XmlAccessorType(XmlAccessType.NONE)
public class RulePOJO
{
#XmlElement(namespace = "urx:xxxxx")
private final String name = null;
#XmlElement(namespace = "urx:xxxxx")
private final String errorType = null;
#XmlElement(namespace = "urx:xxxxx")
private final Date startDate = null;
#XmlElement(name = "listId1", namespace = "urx:xxxxx")
private final List<Long> listId1 = new ArrayList<>();
#XmlElement(name = "listId2", namespace = "urx:xxxxx")
private final List<Long> listId2 = new ArrayList<>();
public String getName()
{
return name;
}
public errorType getErrorType()
{
return errorType;
}
public Boolean getFlag()
{
return flag;
}
public Date getStartDate()
{
return startDate;
}
public List<Long> getListId1()
{
return listId1;
}
public List<Long> getListId2()
{
return listId2;
}
}
the business class used for unmarshalling xml into the pojo
public class Retriever() throws JAXBException
{
Document document = Configuration.load(URI.create("urn:xxxxxx"));
Unmarshaller unmarshaller = JAXBContext.newInstance(RulesPOJO.class).createUnmarshaller();
RulesPOJO rulesPOJO = (RulesPOJO) unmarshaller
.unmarshal(document.getElementsByTagName("Rules").item(0));
for(RulePOJO rulePOJO : RulesPOJO.getRulesPOJO())
{
// the following are coming back null
List<Long> listId1 = rulePOJO.getListId1();
List<Long> listId2 = rulePOJO.getListId2();
}
}
Thanks!
As discussed in the comments, id is the repeating element to be treated as arraylist so as mentioned in the given link https://howtodoinjava.com/jaxb/xmlelementwrapper-annotation/. You can use XMLElementWrapper and XMLElement types of annotations to denote wrapper and child elements.
I would like to deserialize and map to class following values by name attribute.
This is piece of my XML file.
<custom-attributes>
<custom-attribute name="Name1" dt:dt="string">VALUE</custom-attribute>
<custom-attribute name="Name2" dt:dt="string">
<value>1111</value>
<value>1111</value>
<value>1111</value>
</custom-attribute>
<custom-attribute name="Name3" dt:dt="string">VALUE2</custom-attribute>
<custom-attribute dt:dt="boolean" name="Name3">VALUE3</custom-attribute>
<custom-attribute dt:dt="boolean" name="Name4">VALUE4</custom-attribute>
</custom-attributes>
And This is piece of my pojo class
#JsonIgnoreProperties(ignoreUnknown = true)
public class CustomAttributes {
#JacksonXmlProperty(localName="name3", isAttribute = true)
private String roleID;
public String getRoleID() {
return roleID;
}
public void setRoleID(String roleID) {
this.roleID = roleID;
}
}
Do you know ho to properly read values from those attribues by name ? Currently im receiving null
I am not sure what the result is supposed to look like, but if you want
to parse the complete xml into matching objects they would look like this:
public class CustomAttributeList {
#JacksonXmlProperty(localName = "custom-attributes")
private List<CustomAttributes> list;
...
}
#JacksonXmlRootElement(localName = "custom-attribute")
public class CustomAttributes {
// the name attribute
#JacksonXmlProperty
private String name;
// the datatype from the dt:dt field
#JacksonXmlProperty(namespace = "dt")
private String dt;
// the content between the tags (if available)
#JacksonXmlText
private String content;
// the values in the content (if available)
#JacksonXmlProperty(localName = "value")
#JacksonXmlElementWrapper(useWrapping = false)
private List<String> values;
...
}
Note that the localName="name3" from your question is not referring to a property at all.
I'm stuck. How to parse Node with same name child node?
In this example i need nodes with rate attribute.
<xml>
<Rates>
<Rates winrate_cap="9999">
<Rates rate="323"/>
<Rates rate="343"/>
<Rates rate="2338"/>
<Rates rate="233"/>
</Rates>
</Rates>
</xml>
My response wrapper class:
#Root(name = "xml", strict = false)
public class XMLResponse {
#ElementList(entry = "Rates")
public List<Rates> response;
public static class Rates {
#Attribute(name = "winrate_cap", required = false)
public String winrate_cup;
#ElementList(required = false, entry = "Rates")
public List<Rates> rates;
}
public static class Rates {
#Attribute(name = "rate", required = false)
public String rate;
}
}
You are on the right way. As you have many Rates here, better name the class more with more context and set the xml name via Annotation.
I've separated the XMLResponse and Rates classes, but this doesn't make any difference.
The class XMLRespone maps the <xml>...</xml> part:
#Root(name = "xml")
public class XMLRespone
{
#Element(name = "Rates")
private Rates rates;
// ...
}
All types for <Rates...>...</Rates> is done by Rates class (and it's inner classes, which map the structure:
#Root(name = "Rates")
public class Rates
{
#Element(name = "Rates")
private RateList rates;
// ...
public static class RateList
{
#ElementList(entry = "Rates", inline = true)
private ArrayList<RateValue> values;
#Attribute(name = "winrate_cap", required = true)
private int winrateCap;
// ...
}
public static class RateValue
{
#Attribute(name = "rate", required = true)
private int rate;
// ...
}
}
Deserializing your XML gives this output (using generated toString()):
XMLRespone{rates=Rates{rates=RateList{values=[RateValue{rate=323}, RateValue{rate=343}, RateValue{rate=2338}, RateValue{rate=233}], winrateCap=9999}}}
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.
I am converting xml file to java object using simpleframework. I am using generics since my xml's internal nodes gets changing. But simpleframework throws an error while converting. Here is my sample code:
#Root(name = "searchresult", strict = false)
public class ResponseVO<T>
{
#ElementList(entry = "document", inline = true)
public List<T> elementVOList = new ArrayList<T>();
public List<T> getElementsVOList()
{
return elementVOList;
}
public void setElementsVOList(List<T>list)
{
elementVOList = list;
}
}
ResponseVO is container for various other VOs as below:
#Root(name = "document", strict = false)
public class Projects_Display_VO
{
#Element(name = "projectname")
private String projectName;
#Attribute(name = "id")
private int tmpid;
public int getTmpid()
{
return tmpid;
}
public void setTmpid(int tmpid)
{
this.tmpid = tmpid;
}
/**
* ProjectId
*/
#Element(name = "projectid")
private String projectID;
public String getProjectName()
{
return projectName;
}
public void setProjectName(String projectName)
{
this.projectName = projectName;
}
public int getProjectID()
{
return Integer.parseInt(projectID);
}
public void setProjectID(String projectID)
{
this.projectID = projectID;
}
}
And the XML file is as below:
<searchresult>
<query>id:(PROJ2 PROJ6)</query>
<document id="0">
<projectid>2</projectid>
<projectname>Redundant Demo Project</projectname>
<doctype>Projects</doctype>
<summary>||Demo Project</summary>
<title>Redundant Demo Project</title>
</document>
<document id="1">
<projectid>6</projectid>
<projectname>Redundant Demo Project2</projectname>
<doctype>Projects</doctype>
<summary>||Main terminal links.</summary>
<title>Terminal 5 Project</title>
</document>
</searchresult>
The code for conversion is as bellow:
ResponseVO<Projects_Display_VO> resp = (ResponseVO<Projects_Display_VO>) SerializationUtil.deserialize(ResponseVO.class, reader);
Here i am using serializer from simpleframework. But it throws following error
Exception::Attribute 'id' does not have a match in class java.lang.Object at line 5
org.simpleframework.xml.core.AttributeException: Attribute 'id' does not have a match in class java.lang.Object at line 5
at org.simpleframework.xml.core.Composite.readAttribute(Composite.java:555)
at org.simpleframework.xml.core.Composite.readAttributes(Composite.java:474)
at org.simpleframework.xml.core.Composite.readSection(Composite.java:387)
I dont know whats going wrong here.A it works fine without generics.
Thanks in advance
This is caused by erasure, T is not available at runtime. Java does not allow it.