i have little bit trouble with deserialization of XML. I am only able to deserialize this xml by:
#JacksonXmlProperty(localName = "field")
#JacksonXmlElementWrapper(useWrapping = false)
List<Object> field;
This is my xml:
<Response>
<user>
<field attribute="x"></field>
<field attribute="y">false</field>
<field attribute="z">string</field>
</user>
<user>
<field attribute="x"></field>
<field attribute="y">false</field>
<field attribute="z">string</field>
</user>
</Response>
Problem is, that i want to replace Object in List<Object> field; with some specific class so i could access attribute and the value in field.
With Object, i am able to create something like this:
user='[{attribute=x}, {attribute=y, =false}, {name=z, =string}]
thanks a lot.
Found answer. I created new class that contains this elements:
#JacksonXmlProperty(isAttribute = true, localName = "attribute")
String attribute;
#JacksonXmlText
String value;
And replace the Object with this new class.
Related
I'm working with jaxb and with xml I'm doing jpa. I had such a question. I keep a list of records in the Catalog. Previously, it was a list from a particular entity, but now I want to be able to do any entity. Tell me how can I do this?
I want to specify in the root element a class with an entity and somehow apply this to the list
#XmlAccessorType(XmlAccessType.FIELD)
#XmlRootElement(name = "catalog")
public class Catalog {
#XmlAttribute
private String id;
#XmlAttribute(name = "class")
private String className;
#XmlElement(name = "record")
private List<Book> recordList = new ArrayList<Book>();
<--Getters and Setters-->
This my XML.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalog id="1" type="extendable" class="cataloghandler.entities.Books">
<record>
<title>book1</title>
<description>hello12345</description>
</record>
<record>
<title>book2</title>
<description>goodbye</description>
</record>
</catalog>
And I want to add another Entity, for example, TVShow, with another fields.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<catalog id="6" type="extendable" class="cataloghandler.entities.TVShow">
<record>
<channel>fox</channel>
<time>10:45</time>
</record>
<record>
<channel>abc</channel>
<time>12:00</time>
</record>
</catalog>
How I can change code so, that List have type List<TVShow>.
I'm working with an API which returns XML something like this:
<xyz-objects version="1.0">
<object pk="1" model="roll">
<field type="BigIntegerField" name="roll_number">1000000714</field>
<field type="CharField" name="status">DL</field>
<field name="scans">
<object pk="1" model="scan_stages">
<field type="DateTimeField" name="updated_on">11 Jul, 2017, 17:40</field>
</object>
</field>
</object>
</xyz-objects>
I'm using Jackson's XML mapper.
I tried writing a POJO object for this but that doesn't work.
How do I handle attributes of elements like type and name?
Can you exemplify?
Based on the investigation I did in https://github.com/FasterXML/jackson-module-kotlin/issues/138 here's a workaround that's a bit more typed that the JsonNode workaround by madhead
data class Field(
#JacksonXmlProperty(isAttribute = true)
val type: String,
#JacksonXmlProperty(isAttribute = true)
val name: String,
) {
#JacksonXmlText
lateinit var value: String private set
}
As you didn't provide the schema, it's hard to guess some parts of your model. Especially, it's hard to guess field model. So I will deserialize it into a JsonNode, dynamic object that can handle literally everything. JsonIgnoreProperties just in case you have some extra fields, not mentioned here.
First, create some data classes:
#XmlRootElement(name = "xyz-objects")
#JsonIgnoreProperties(ignoreUnknown = true)
data class XyzObjects(
#JacksonXmlProperty(isAttribute = true)
val version: String,
#JsonProperty(value = "object")
#JacksonXmlElementWrapper(useWrapping = false)
val objects: List<Object>
)
#XmlRootElement(name = "object")
#JsonIgnoreProperties(ignoreUnknown = true)
data class Object(
#JacksonXmlProperty(isAttribute = true)
val pk: String,
#JacksonXmlProperty(isAttribute = true)
val model: String,
#JsonProperty(value = "field")
#JacksonXmlElementWrapper(useWrapping = false)
val fields: List<JsonNode>
)
Answering your question: you can handle attrributes with by using isAttribute attribute of JacksonXmlProperty. Lists can be handled with JacksonXmlElementWrapper + JsonProperty (don't be fooled by name, it is used to handle plurals in names, thus making them more human readable in XML too).
In order to be able to work with data classes, you'll need to register Kotlin module with registerKotlinModule:
val text = """<xyz-objects version="1.0">
<object pk="1" model="roll">
<field type="BigIntegerField" name="roll_number">1000000714</field>
<field type="CharField" name="status">DL</field>
<field name="scans">
<object pk="1" model="scan_stages">
<field type="DateTimeField" name="updated_on">11 Jul, 2017, 17:40</field>
</object>
</field>
</object>
</xyz-objects>"""
val mapper = XmlMapper().registerKotlinModule()
val node = mapper.readValue<XyzObjects>(text)
println(node)
It will print:
XyzObjects(version=1.0, objects=[Object(pk=1, model=roll, fields=[{"type":"BigIntegerField","name":"roll_number","":"1000000714"}, {"type":"CharField","name":"status","":"DL"}, {"name":"scans","object":{"pk":"1","model":"scan_stages","field":{"type":"DateTimeField","name":"updated_on","":"11 Jul, 2017, 17:40"}}}])])
I need to convert data retrieved from Solr to specific class, I've tried 3 different ways, but each of them has some problem.
Method 1 : using 3rd party library - JSONUtil.
SolrDocument solrDocument = (SolrDocument) iterator.next();
String jsonString = JSONUtil.toJSON(solrDocument);
EventObject eventObject2 = gson.fromJson(jsonString, EventObject.class);
Method 2 : transform solrDocument to JsonString directly.
String jsonString2 = gson.toJson(solrDocument);
EventObject eventObject3 = gson.fromJson(jsonString2, EventObject.class);
Method 3 : transform solrDocument to JSONObject then convert to JsonString.
JSONObject object = new JSONObject(solrDocument);
String jsonStringFromJsonObject = object.toString();
EventObject eventObjectFromJsonObject = gson.fromJson(jsonStringFromJsonObject, EventObject.class);
Method 4 : using Java Beans
List<EventObject> EventObjects = response.getBeans(EventObject.class);
The result of Method1 can't handle fields which is Date or ArrayList Type. The result of Method2, Method3 and Method4 are all the same, they handle all fields well, however, ArrayList field are still empty.
Edit :
My Solr schema is as follows(only mention the 2 fields which can't transform correctly) :
<dynamicField name="*_Image_Infos" type="string" indexed="true" stored="true" required="false" multiValued="true" />
<dynamicField name="*_ss" type="string" indexed="true" stored="true" required="false" multiValued="true" />
My EventObject is as follows :
public List<EventObjectImageInfo> ImageInfos = new ArrayList<EventObjectImageInfo>();
public List<EventObjectColor> Colors = new ArrayList<EventObjectColor>();
Both of EventObjectImageInfo & EventObjectColor are my own classes.
Did I do anything wrong? Or is there any other way should I try? Thanks!
You can use Solr JavaBeans
Let's say i have the solr schema :
<field name="id" type="string" multiValued="false" indexed="true" required="true" stored="true"/>
<field name="name" type="string" indexed="false" stored="true"/>
<field name="marks" type="long" multiValued="true" indexed="true" stored="true"/>
I have the Student class
public class Student {
#Field
String id;
#Field
String name;
#Field
ArrayList<Long> marks;
}
Now I can convert SolrDocumentList to List with javabeans
SolrDocumentList list; // you already have that
DocumentObjectBinder binder = new DocumentObjectBinder();
List<Student> dataList = binder.getBeans(Student.class, list);
I ve a confusion regarding how the dozer maps the source object to destination object. I have the following scenario:
Source Object:
public class Rule {
private String id;
private String name;
private String group;
private String content;
private RuleType ruleType;
private String drlContent;
private boolean enabled;
private Strategy strategy;
// getters and setters
}
Destination Object:
public class RuleActivity {
private String id;
private String name;
private Strategy strategy;
// getters and setters
}
XML Mapping:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<stop-on-errors>true</stop-on-errors>
<date-format>MM/dd/yyyy HH:mm</date-format>
</configuration>
<mapping wildcard="false">
<class-a>com.magick.models.shared.Rule</class-a>
<class-b>com.magick.models.shared.log.RuleActivity</class-b>
<field>
<a>id</a>
<b>ruleId</b>
</field>
<field>
<a>strategy.name</a>
<b>strategy.name</b>
</field>
<field>
<a>name</a>
<b>name</b>
</field>
</mapping>
Now How these would be mapped ? I mean , does the destination Object contains the Complete Strategy Object or only the strategy.name field of it.
First of all, by default dozer mappings are bi-directional. So,
mapping from class-a to class-b and vice-versa is permitted.
As you have done your mapping as follows:
<field>
<a>strategy.name</a>
<b>strategy.name</b>
</field>
If the source object is having a Strategy object which is not null and have all the relevant field's value. Then dozer will create a new Strategy object for destination as well and will only populate the name field of newly created Strategy object.
Further, dozer also works on retrospection so suppose the name and type of all fields of Strategy object in source and destination is same. Dozer will map or copy all the fields automatically. So you don't have to map each field individually. you just have to write as below.
<field>
<a>strategy</a>
<b>strategy</b>
</field>
But if your field names or type is not same, you need to define mapping for each field as you did for id field for Rule class and ruleId field for RuleActivity class.
<field>
<a>id</a>
<b>ruleId</b>
</field>
Hope this clarifies your doubts.
I'm using Java & GAE datastore to store, manage and retrieve some data for my application.
Basically I have two classes: Customer and Store. The idea is that one customer can have more than one store associated, and many customers can exist.
The structure of a customer is something like this:
<Customer>
<id>id1</id>
<Stores>
<Store>
<storeId>100</storeId>
<city>Venice</city>
</Store>
<Store>
<storeId>200</storeId>
<city>Milan</city>
</Store>
<Store>
<storeId>300</storeId>
<city>Venice</city>
</Store>
</Stores>
</Customer>
As I said before, there can be MANY customers:
<Customers>
<Customer>
...
</Customer>
<Customer>
...
</Customer>
</Customers>
I need to build a filter to get only a portion of these customers, passing to a query a "CITY" parameter:
For example, if I want to show Customers that has at least one Store located in Venice, I would do something like ( just to give you the idea)
GET Customer WHERE Customer.Store.City = 'Venice'
What I'd like to get is every Customer that has a Store located in a particular city.. but also, the Customer Object needs to have those stores! like this:
<Customer>
<id>id1</id>
<Stores>
<Store>
<storeId>100</storeId>
<city>Venice</city>
</Store>
<Store>
<storeId>300</storeId>
<city>Venice</city>
</Store>
</Stores>
</Customer>
<Customer>
<id>id2</id>
<Stores>
<Store>
<storeId>700</storeId>
<city>Venice</city>
</Store>
</Stores>
</Customer>
I can get those stores correctly, but I need to find a way to connect every store to his ancestor customer..
String city = 'something';
Query query = mgr.newQuery(Store.class, "city == '"+city+"' ");
List<Store> oggetti = (List<Store>) query.execute();
Any idea on how to do this?
I hope I was clear enough.. thanks in advance, best regards
Additional info:
class Customer:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
#Cacheable("false")
#FetchGroup(name="customerstores", members={#Persistent(name="storeList")})
public class Customer {
#PrimaryKey
#Persistent
private String id= "";
#Persistent
#Unowned
private List<Store> storeList;
//getters and setters here
....
}
Class Store:
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
#Cacheable("false")
public class Store {
#PrimaryKey
#Persistent
private String storeUniqueKey = "";
#Persistent
private String city = "";
//getters and setters here
....
}
See if following code could fit your requirement.
Query query = pm.newQuery(Customer.class);
query.declareVariables("Customer store");
query.setFilter(
"this.stores.contains(store) && store.city == \"SomeCity\"");
Collection result = (Collection)query.execute();