I am using SimpleXML for Java to parse a XML response to java class mapping. However, I am not able to get this particular piece working with my android device.
My XML fragment looks like this,
<t:EmailAddresses>
<t:Entry Key="EmailAddress1">sip:xxx#abs.com</t:Entry>
<t:Entry Key="EmailAddress2">smtp:xxx#abs.com</t:Entry>
<t:Entry Key="EmailAddress3">SMTP:xxx#abs.com</t:Entry>
</t:EmailAddresses>
and my Class definition for EmailAddresses looks like this,
#Root
public class EmailAddresses
{
#ElementList
private List<Entry> Entry;
public List<Entry> getEntry() { return Entry; }
public void setEntry(List<Entry> entry) { Entry = entry; }
}
And my Entry class looks like this,
#Element
public class Entry
{
#Attribute
private String Key;
public String getKey() { return Key; }
public void setKey(String key) { Key = key; }
}
when I parse run the parser, I only get the Keys and that also, I get "Multiple Root Elements" error when trying to parse all 3 into a List of Entry class.
Can someone please point me in the right direction?? Thanks !!
Note: The XML Namespace "t" is defined properly.
Here are some things you should change:
Class EmailAddresses
#Root(name = "EmailAddresses") /* 1 */
#Namespace(prefix = "t", reference = "INSERT YOUR REFERENCE HERE!") /* 2 */
public class EmailAddresses
{
#ElementList(inline = true) /* 3 */
private List<Entry> Entry;
// ...
}
Explanation:
/* 1 */: Set the name of the element (case sensitive); simple does this per default, but so you can ensure it's really correct.
/* 2 */: Set the namespace and it's reference; required for the t in your XML.
/* 3 */: Inline the list; the <t:EmailAddresses> element is constructed out of the #Root() element, all entries follow as
inline-elements. Otherwise the list will create another element as child,
wrapping it's entries.
Class Entry
#Root(name = "Entry") /* 1 */
#Namespace(prefix = "t", reference = "INSERT YOUR REFERENCE HERE!") /* 2 */
public class Entry
{
#Text
private String text; /* 3 */
#Attribute
private String Key;
// ...
}
Explanation:
/* 1 */: Don't use #Element here, use #Root().
/* 2 */: As #2 above.
/* 3 */: Your Entry-tags in the XML contain text (= value of the element, like the "sip:..."), those require a mapping too. If the text is optional, you can use #Text(required = false) to indicate that.
TIP: Create an instance of your list, fill it with entries and serialize it, e.g. into a file. So you can see if the mapping is matching your expectations, and where you have to do some corrections.
Can you not use attributes to extract the data? Here's a snippet from an RSS feed reader app that parses XML similarly:
if (localName.equals("channel"))
{
/** Get attribute value */
String attr = attributes.getValue("category");
itemList.setTitle(attr);
}
Related
Let's assume we have following entities:
Item:
class Item {
...
#Index(unique=true)
private String guid;
...
#ToMany
#JoinEntity(entity = JoinItemsWithTags.class, sourceProperty = "itemGuid", targetProperty = "tagName")
private List<Tag> tagsWithThisItem;
...
}
Tag:
class Tag {
#Id
private Long localId;
#Index(unique = true)
private String name;
...
}
and we need to join them. Here is my join entity class:
#Entity(nameInDb = "item_tag_relations")
class JoinItemsWithTags {
#Id
private Long id;
private String itemGuid;
private String tagName;
...
}
I want to use tag name as a join property instead of Long id, because it's easier to support consistency when syncing with server.
But currently tags getter in Item class always return an empty list. I've looked into log and found generated query which using internally in that getter:
SELECT * <<-- there were a long sequence of fields
FROM "tags" T JOIN item_tag_relations J1
ON T."_id"=J1."TAG_NAME" <<-- here is the problem, must be `T."NAME"=J1."TAG_NAME"`
WHERE J1."ITEM_GUID"=?
So the problem is that join is base on tag's _id field. Generated List<Tag> _queryItem_TagsWithThisItem(String itemGuid) method implicitly uses that id to make a join:
// this `join` nethod is overloaded and pass tag's id as source property
queryBuilder.join(JoinItemsWithTags.class, JoinItemsWithTagsDao.Properties.TagName)
.where(JoinItemsWithTagsDao.Properties.ItemGuid.eq(itemGuid));
Correct approach is this case might be following, I suppose:
// source property is passed explicitly
queryBuilder.join(/* Desired first parameter -->> */ TagDao.Properties.Name,
JoinItemsWithTags.class, JoinItemsWithTagsDao.Properties.TagName)
.where(JoinItemsWithTagsDao.Properties.ItemGuid.eq(itemGuid));
But this code is in generated dao, and I don't know how to do anything with it. Is there any way to workaround this?
I am writing an annotation processor in android which generates a java file. I am using JavaPoet library for that.
The purpose of generated file:
It should have a list of names of the classes with a particular annotation that my processor supports and provide a public method to get that list.
Now, I've generated the file:
private final List<String> names;
GeneratedFile(ArrayList<String> names) {
this.names = names;
}
public List<String> getNames() {
return names;
}
Now, the problem is: How do I initialize the names field from the processor? The Javapoet api provides an initializer for the field but that only takes a string.
In my processor, I've the list of classes that have my supported annotation. I want to populate this field with that list.
As you already know, JavaPoet offers only a string to specify field initialization. To accomplish your task you have to (I will show you some code that is not specific to your problem, but it can be a good example to show you how to do it):
Get from your annotation processor the list of classes with your annotation (the code is copied from a my library):
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
model = new PrefsModel();
parseBindType(roundEnv);
// Put all #BindSharedPreferences elements in beanElements
for (Element item : roundEnv.getElementsAnnotatedWith(BindSharedPreferences.class)) {
AssertKripton.assertTrueOrInvalidKindForAnnotationException(item.getKind() == ElementKind.CLASS, item, BindSharedPreferences.class);
// store type name somewhere
}
return true;
}
Use this set of TypeName to generate the initial value for you field:
...
Builder sp = FieldSpec.builder(ArrayTypeName.of(String.class), "COLUMNS", Modifier.STATIC, Modifier.PRIVATE,
Modifier.FINAL);
String s = "";
StringBuilder buffer = new StringBuilder();
for (SQLProperty property : entity.getCollection()) {
buffer.append(s + "COLUMN_" +
columnNameToUpperCaseConverter.convert(property.getName()));
s = ", ";
}
classBuilder.addField(sp.addJavadoc("Columns array\n").initializer("{" +
buffer.toString() + "}").build());
...
You will find my library on github. In particular, you have to read the class com.abubusoft.kripton.processor.sqlite.BindTableGenerator.
Hope this information can be still useful.
I'm using Retrofit 2 with a SimpleXmlConverter and I am facing an issue when creating a Soap Request Object, that is basically an element with 4 element children each one of them being different datatypes.
Here is the XML output I want to produce. The element order must be respected:
<prf:container>
<prf:aaa>111111111</prf:aaa>
<prf:bbb>true</prf:bbb>
<prf:element>
<prf:ddd>50</prf:ddd>
<prf:eee>false</prf:eee>
</prf:element>
<prf:ccc>textcontent</prf:ccc>
</prf:container>
Now, here is my Android Class, Container.java, representing the Soap Request Object that will be serialized:
#Root (name = "prf:container")
#Order(elements={"prf:aaa", "prf:bbb", "prf:element", "prf:ccc"})
public class Container {
#Element (name = "prf:aaa")
private int aaa;
#Element(name = "prf:bbb")
private boolean bbb;
#Element (name = "prf:element", required = false)
private MyElement myElement;
#Element (name = "prf:ccc", required = false)
private String ccc;
}
According to the Simple XML framework documentation:
By default serialization of fields is done in declaration order.
However, in Android, this is not true, at least in some cases. No matter how I set the field declaration order in my Container class, the output has always the same element order. This is a known bug and as has been reported in other SO posts.
Nonetheless, there is a solution to this issue. The Order annotation.
Read more in the Javadoc.
My problem is that using the Order annotation in my case is not helping. Note that all my elements have a prefix on its name - prf:.
If I remove the prf prefix from all my element names, Order annotation will work properly, and force the XML Serialization to have the defined order. But the output elements won't have the prefix on its name.
But I really need my elements to have the prefix on its name, or else my request will have a 500 response. I also have to have the desired element order in my XML output.
Any solution to this?
Thank you
I know it has been a long item since you posted this question but, I would like to answer your question in case anyone faced the same issue. I solved the same issue by doing the following:
For the XML document to be prepared with the elements in the order you want and if the elements have a prefix, #Order annotation might not work in some cases. In your case, the prefix 'prf' mentioned in the #Order annotation for each element would not work to order them as you desired.
"By default serialization of fields is done in declaration order."
I don't believe this either, especially when you have prefixes for elements. So, I tried changing the Java variable names. I tried naming them in alphabetical order in the same way I needed them in the generated xml. So, in your case, you can change the variable names as follows:
#Root (name = "prf:container")
public class Container {
#Element (name = "prf:aaa")
private int element1;
#Element(name = "prf:bbb")
private boolean element2;
#Element (name = "prf:element", required = false)
private MyElement element3;
#Element (name = "prf:ccc", required = false)
private String element4;
}
This would form the xml document exactly as you wanted. You might wonder that if we change the variable names to be too generic, they are not representing what they actually are but, you can always have getters and setters. For example, in your case you can have:
public void setAaa(String aaa){
this.element1 = aaa;
}
public String getAaa(){
return element1;
}
In the same way you can always generate the classes with alphabetically ordered variables to make sure the generated xml has the elements in the desired format.
Maybe you using #Order with wrong syntax,Alphabetical order is not important. You can try:
#Root (name = "prf:container")
#Order(elements={"prf:container/prf:aaa", "prf:container/prf:bbb", "prf:container/prf:element", "prf:container/prf:ccc"})
public class Container {
#Element (name = "prf:aaa")
private int aaa;
#Element(name = "prf:bbb")
private boolean bbb;
#Element (name = "prf:element", required = false)
private MyElement myElement;
#Element (name = "prf:ccc", required = false)
private String ccc;
}
SimpleXML's auto ordering by alphabetical order is working. But on one condition: the type of those fields should be the same, usually for XML it is String. It took me long time to figure that out, I had different types, and ordering by name didn't work. Since I've changed all fields to String works like a charm.
#Root(name = "sch:CheckPaymentRequest", strict = true)
public class CheckPaymentData {
#Element(name = "sch:payId")
private String Aaa1;
#Element(name = "sch:fromCurrency")
private String Bbb2;
#Element(name = "sch:fromAmount")
private String Ccc3;
...}
I'm trying to map an object to database with greenDao. But when it comes to arrays, I don't know how to do it. After receiving JSON from network and deserializing it with GSON, I have objects defined by this class:
public class Car {
Long carId;
String name;
ArrayList<String> listOfLinks;
}
In case of a a different architecture, like this:
public class Car {
Long carId;
String name;
ArrayList<Link> listOfLinks;
}
public class Link {
Long carId;
String link;
}
----
Entity cars = schema.addEntity("Car");
cars.addLongProperty("carId").primaryKey();
cars.addStringProperty("name");
Entity links = schema.addEntity("Link");
links.addStringProperty("name");
links.addIdProperty().primaryKey().notNull().autoincrement();
Property linkProperty = links.addLongProperty("carId").getProperty();
ToMany carToLinks = cars.addToMany(link, linkProperty);
It would is easy. Define some relations, define properties, add foreign key and your done. With arrays I have no clue what to do. Ideas?
That approach is not common when using relational databases.
This is commonly done using to-many relations : instead of using a list of String, you can create a Link entity and then use a list of Link.
Relation toMany is useful when you have a list of your not primitive object, that you can declare like entity that have its own id etc etc etc, and make list of entities (with toMeny). By doing that greenDao makes another table in the base for you new entity with the foreign key of the base entity that contains list. When you have list of primitive type the only way to do is to make converter that converts List into one of the primitive types that greenDao works naturally. You have to do something like this `
import org.greenrobot.greendao.converter.PropertyConverter;
import java.util.Arrays;
import java.util.List;
/**
*DOLE BREEE SQLITE BREEEEEE!!!**
*i choosed to convert List into one string
*that is going to be saved in database, and vice versa
*/
public class GreenConverter implements PropertyConverter, String> {
#Override
public List convertToEntityProperty(String databaseValue) {
if (databaseValue == null) {
return null;
}
else {
List<String> lista = Arrays.asList(databaseValue.split(","));
return lista;
}
}
#Override
public String convertToDatabaseValue(List<String> entityProperty) {
if(entityProperty==null){
return null;
}
else{
StringBuilder sb= new StringBuilder();
for(String link:entityProperty){
sb.append(link);
sb.append(",");
}
return sb.toString();
}
}
}
now above all the properties that are List you have to put
#Convert(converter=yourconverterclass.class, columnType = String.class)
#Entity
public class ShipEntry {
#Id(autoincrement = true)
private long ship_id;
private String name;
private String model;
private String manufacturer;
private String starship_class;
#Convert(converter = GreenConverter.class, columnType = String.class)
private List<String> pilots;
#Convert(converter = GreenConverter.class, columnType = String.class)
private List<String> films ;
}
you can create Converter as a inner class of entitiy, and in that case it has to be declared as staticthat is the only way i have found, but the bad side is that you can not use property that you are converting into query. There might me some typo, but i hope this helps to solve your problem
I also have the same issue, and there no answer (not in official docs, not in google). Please explain how to map List to Entity?
public class Car {
Long carId;
String name;
ArrayList<String> listOfLinks;
}
Can I do something like this?
#Entity(active = true, nameInDb = "CARS")
public class Car {
#Id
private Long id;
#NotNull
#Unique
private String remoteId;
#ToMany(joinProperties = {
#JoinProperty(name = "remoteId", referencedName = "carRemoteId")
})
private List<Links> listOfLinks;
}
#Entity(active = true, nameInDb = "LISTOFLINKS")
public class Links{
#Id
private Long id;
#NotNull
#Unique
private String remoteId;
#SerializedName("listOfLinks")
#Expose
private String listOfLinks;//is it possible?????
private String carRemoteId;
}
Since JPA 2.0, you can use an element collection to persist a Collection of value types. You just need to annotate the attribute with #ElementCollection and the persistence provider will persist the elements of the Collection in an additional database table.
#Entity
public class Author {
#ElementCollection
private List<String> phoneNumbers = new ArrayList<String>();
}
The element collection might seem easier to use than an entity with a one-to-many association. But it has one major drawback: The elements of the collection have no id and Hibernate can’t address them individually.
When you add a new Object to the List or remove an existing one, Hibernate deletes all elements and inserts a new record for each item in the List.
Let’s take a quick look at an example. The following code snippet selects an Author entity and adds a second phoneNumber to the element collection.
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
Author a = em.find(Author.class, 1L);
a.getPhoneNumbers().add("42424242");
em.getTransaction().commit();
em.close();
an element collection is an easy but not the most efficient option to store a list of value types in the database. You should, therefore, only use it for very small collections so that Hibernate doesn’t perform too many SQL statements. In all other cases, a one-to-many association is the better approach.
on Android I'm using SimpleFramework to parse incoming XML and create appropriate objects (which are saved to DB afterwards...)
A part of XML looks like this:
<CheckId xmlns:a="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<a:guid>00300001-0000-0000-0000-000000000000</a:guid>
</CheckId>
Where <a:guid> can repeat 1..N
My element annotations look like this:
#ElementList(required = false, name="CheckId")
#Namespace(reference="http://schemas.microsoft.com/2003/10/Serialization/Arrays")
#ForeignCollectionField(eager = true, orderColumnName = "Guid", columnName = TABLE_CHECK_ID_LIST_COLUMN)
public Collection<TableCheck> TableCheckIdList;
and in the TableCheck class is:
#Root(name = "CheckId")
#Order(elements = { "guid" })
public static class TableCheck implements XMLParseable {
#Element(required = false, name="guid")
#DatabaseField
public String Guid;
...
}
After parsing is done, the Collection contains as many items as tag <a:guid> appeared in the XML. However, the property Guid is always NULL.
I've tried to play with Namespace / Prefix attributes but the result is always the same - NULL value in Guid property.
Any ideas?
This has nothing to do with the #Namespace annotation. Here Guid is null because of one of the following reasons.
a) It is written like <a:guid/>
b) It is written line <a:guid></a:guid>
c) Something is setting it to be null after you have finished parsing it.
Try using constructor injection and set the value as final, then you know nothing else is setting it to null after the object is created.