Simpleframework. Can nulls be retained in collections? - java

I have a object -> XML -> object process in one project I have to support.
The object is containing List and if it gets serialized, all the null values which where present in list are omitted.
My question is, can it be done with Simpleframework or should I use something else? What?
Here is what I do:
import java.io.StringWriter;
import java.util.Arrays;
import java.util.List;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.core.Persister;
import org.testng.annotations.Test;
public class SimpleframeworkTest {
#Test
public void testNullsInParams() throws Exception {
Container container = new Container();
container.setId(4000);
container.setParams(Arrays.asList(new Object[] { "foo", null, "bar" }));
String xml = container.toXml(); // omits null value in output
}
#Test
public void testDeserializeNull() throws Exception {
String xml = "<container id=\"4000\">"+
" <object class=\"java.lang.String\">foo</object>"+
// " <object class=\"java.lang.String\"></object>"+ // gets NullPointerException here
" <object class=\"java.lang.String\">bar</object>"+
"</container>";
Container object = Container.toObject(xml);
}
#Root(name = "container", strict = false)
public static class Container {
#Attribute
private Integer id;
#ElementList(inline = true, required = false)
private List<Object> params;
public String toXml() throws Exception {
StringWriter sw = new StringWriter();
new Persister().write(this, sw);
return sw.toString();
}
public static Container toObject(String xml) throws Exception {
return new Persister().read(Container.class, xml);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Object> getParams() {
return params;
}
public void setParams(List<Object> params) {
this.params = params;
}
#Override
public String toString() {
return "Container [id=" + id + ", params=" + params + "]";
}
}
}

First, your list annotation is missing the the entries name:
#ElementList(inline = true, required = false, entry = "object")
private List<Object> params;
Otherwise <string>...</string> is used, not <object>...</object>.
You can prevent that nullpointer excpetion by adding type = String.class to your list's annotation. However, this doesn't fix the main problem.
In general empty tags / null-elements will not be added to the result.
Here's an example how to solve this problem with a Converter.
public class SimpleframeworkTest
{
// ...
#Root(name = "container", strict = false)
#Convert(NullawareContainerConverter.class)
public static class Container
{
static final Serializer ser = new Persister(new AnnotationStrategy());
// ...
public String toXml() throws Exception
{
StringWriter sw = new StringWriter();
ser.write(this, sw);
return sw.toString();
}
public static Container toObject(String xml) throws Exception
{
return ser.read(Container.class, xml);
}
// ...
}
static class NullawareContainerConverter implements Converter<Container>
{
final Serializer ser = new Persister();
#Override
public Container read(InputNode node) throws Exception
{
final Container c = new Container();
c.id = Integer.valueOf(node.getAttribute("id").getValue());
c.params = new ArrayList<>();
InputNode n;
while( ( n = node.getNext("object")) != null )
{
/*
* If the value is null it's added too. You also can add some
* kind of null-replacement element here too.
*/
c.params.add(n.getValue());
}
return c;
}
#Override
public void write(OutputNode node, Container value) throws Exception
{
ser.write(value.id, node);
for( Object obj : value.params )
{
if( obj == null )
{
obj = ""; // Set a valid value if null
}
// Possible you have to tweak this by hand
ser.write(obj, node);
}
}
}
}
As written in the comments, you have to do some further work.
Results:
testNullsInParams()
<container>
<integer>4000</integer>
<string>foo</string>
<string></string>
<string>bar</string>
</container>
testDeserializeNull()
Container [id=4000, params=[foo, null, bar]]

Related

I need Freemarker documentation for data model when using #recurse with trees other than XML

I am trying to process a tree structure in Freemarker and would like use the #recurse, #visit directives but I can't find any good documentation on how to set up the data model. The only examples I can see are those that create a data model for an XML structure. I don't need it to be so detailed. My tree is very simple.
In trying to test the functionality I need, I built a unit test but when I run it I get
FreeMarker template error:
For "." left-hand operand: Expected a hash, but this has evaluated to a node
Here is the source code:
public class FreemarkerXmlTests {
static class Element implements TemplateNodeModel {
private final String name;
private final String text;
private Element parent;
private final List<Element> elements = new ArrayList<>();
public Element(String name) {
this(name, null);
}
public Element(String name, String text) {
this.name = name;
this.text = text;
}
public void add(Element element) {
element.parent = this;
this.elements.add(element);
}
public List<Element> getElements() {
return this.elements;
}
public String getName() {
return this.name;
}
public String getText() {
return this.text;
}
public String getTitle() {
return this.name;
}
public TemplateModel get(String key) {
return null;
}
#Override
public TemplateNodeModel getParentNode() throws TemplateModelException {
return this.parent;
}
#Override
public TemplateSequenceModel getChildNodes() throws TemplateModelException {
// TODO Auto-generated method stub
return new SimpleSequence(this.elements, cfg.getObjectWrapper());
}
#Override
public String getNodeName() throws TemplateModelException {
return this.name;
}
#Override
public String getNodeType() throws TemplateModelException {
return this.name;
}
#Override
public String getNodeNamespace() throws TemplateModelException {
return null;
}
}
private static Configuration cfg;
private static final String myTestTemplate = "<#recurse doc>\r\n" +
"\r\n" +
"<#macro book>\r\n" +
" Book element with title ${.node.title} \r\n" +
" <#recurse>\r\n" +
" End book\r\n" +
"</#macro>\r\n" +
"\r\n" +
"<#macro title>\r\n" +
" Title element\r\n" +
"</#macro>\r\n" +
"\r\n" +
"<#macro chapter>\r\n" +
" Chapter element with title: ${.node.title}\r\n" +
"</#macro>";
#BeforeClass
public static void classInit() throws IOException {
StringTemplateLoader stringTemplateLoader = new StringTemplateLoader();
stringTemplateLoader.putTemplate("myTestTemplate", myTestTemplate);
cfg = new Configuration(Configuration.VERSION_2_3_29);
cfg.setTemplateLoader(stringTemplateLoader);
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
cfg.setLogTemplateExceptions(false);
cfg.setWrapUncheckedExceptions(true);
cfg.setFallbackOnNullLoopVariable(false);
}
#Test
public void basicXmlTest() throws TemplateException, IOException {
Element doc = new Element("doc");
Element book = new Element("book");
book.add(new Element("title", "Test Book"));
doc.add(book);
Element chapter1 = new Element("chapter");
chapter1.add(new Element("title", "Ch1"));
chapter1.add(new Element("para", "p1.1"));
chapter1.add(new Element("para", "p1.2"));
chapter1.add(new Element("para", "p1.3"));
book.add(chapter1);
Element chapter2 = new Element("chapter");
chapter2.add(new Element("title", "Ch2"));
chapter2.add(new Element("para", "p2.1"));
chapter2.add(new Element("para", "p2.2"));
chapter2.add(new Element("para", "p2.3"));
book.add(chapter2);
Map<String, Object> root = new HashMap<>();
// Put string "user" into the root
root.put("doc", doc);
Template temp = cfg.getTemplate("myTestTemplate");
Writer out = new OutputStreamWriter(System.out);
temp.process(root, out);
}
Any ideas?
Take a look at the freemarker.template.TemplateNodeModel interface. Your objects have to implement that, or they have to be wrapped (via the ObjectWrapper) into a TemplateModel the implements that. Then #recurse/#visit/?parent/?children/etc. will work with them.
Here's an example of implementing TemplateNodeModel for traversing JSON: https://github.com/freemarker/fmpp/blob/master/src/main/java/fmpp/models/JSONNode.java
Some templates where above is used:
https://github.com/freemarker/fmpp/tree/master/src/test/resources/tests/dl_json/src
As of the . operator, for that you need to implement TemplateHashModel (or its sub-interfaces, like TemplateHashModelEx2).
With the help of the examples posted by ddekany, I added the following:
implements TemplateHashModel to the Element class
static class Element implements TemplateNodeModel, TemplateHashModel {
a method to the Element class and the unit test worked:
#Override
public TemplateModel get(String key) throws TemplateModelException {
switch (key) {
case "title":
case "name":
return cfg.getObjectWrapper().wrap(this.name);
default:
throw new TemplateModelException("unknown hash get: " + key);
}
}

How to define array of objects in a properties file and read from Java program

I have a properties file like this.
property[0].name=A
property[0].value=1
property[1].name=B
property[1].value=2
property[2].name=C
property[2].value=3
How to read this file as a list of objects of a class {name, value} in plain java program using ResourceBundle or Properties?
Here is the class.
public class XYZ {
private String name;
private String value;
// Getters & Setters
}
I need to get like this.
ArrayList<XYZ> propertiesList = SomeUtility.getProperties("property", XYZ.class);
Utility class might be like this.
public class SomeUtility {
public static ArrayList getProperties(String key, Class cls) {
//logic
}
}
I might not understand exactly what you want so feel free to correct me and give me more constraints to work with but here is a simple way to read a Properties file located somewhere in your project:
private static void readPropertiesFile(String path) throws IOException {
java.util.Map<String, String> map = new java.util.LinkedHashMap<>();
Properties properties = new Properties();
InputStream inputStream = new FileInputStream(path);
properties.load(inputStream);
for (String name : properties.stringPropertyNames()) {
map.put(name, properties.getProperty(name));
}
for (java.util.Map.Entry<String, String> entry : map.entrySet()) {
System.out.printf("Property Key: %s, Property Value: %s%n", entry.getKey(), entry.getValue());
}
}
Output
Property Key: property[0].name, Property Value: A
Property Key: property[1].name, Property Value: B
Property Key: property[0].value, Property Value: 1
Property Key: property[1].value, Property Value: 2
Property Key: property[2].name, Property Value: C
Property Key: property[2].value, Property Value: 3
This is the solution I wrote, but it involves Reflect and Gson. Is there any better way to do this? Anything already available which is fine tuned like Apache's.
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import java.lang.reflect.Field;
import java.util.*;
public class ListResourceBundle {
public static final Gson gson = new Gson();
private final ResourceBundle bundle;
public ListResourceBundle(ResourceBundle bundle) {
this.bundle = bundle;
}
public List<?> getProperties(String key, Class<?> cls) {
final int maxArraySize = getMaxArraySize(key, getMatchingKeys(key));
final List<String> fields = getFields(cls);
final List<Object> result = new ArrayList<>();
for (int i = 0; i < maxArraySize; i++) {
JsonObject jsonObject = new JsonObject();
for (String field : fields) {
jsonObject.addProperty(field, getStringOrNull(key + "[" + i + "]." + field));
}
result.add(gson.fromJson(jsonObject, cls));
}
System.out.println("result.toString() = " + result.toString());
return result;
}
public List<String> getMatchingKeys(String key) {
Enumeration<String> keys = bundle.getKeys();
List<String> matchingKeys = new ArrayList<>();
while(keys.hasMoreElements()) {
String k = keys.nextElement();
if(k.startsWith(key)) {
matchingKeys.add(k);
}
}
Collections.sort(matchingKeys);
return matchingKeys;
}
public int getMaxArraySize(String key, List<String> matchingKeys) {
int maxArraySize = 0;
for (int i = 0; ; i++) {
boolean indexAvailable = false;
for (String matchingKey : matchingKeys) {
if(matchingKey.startsWith(key + "[" + i + "]")) {
indexAvailable = true;
break;
}
}
if(indexAvailable) {
maxArraySize++;
} else {
break;
}
}
return maxArraySize;
}
public String getStringOrNull(String key) {
try {
return bundle.getString(key);
} catch (MissingResourceException e) {
return null;
}
}
public List<String> getFields(Class<?> cls) {
final List<String> fields = new ArrayList<>();
for (Field field : cls.getDeclaredFields()) {
fields.add(field.getName());
}
return fields;
}
public static void main(String[] args) {
ResourceBundle bundle = ResourceBundle.getBundle("com.example.application.resources.Resource");
ListResourceBundle applicationResourceBundle = new ListResourceBundle(bundle);
applicationResourceBundle.getProperties("property", ReportParam.class);
}
}
Resource:
property[0].name=A
property[0].value=1
property[1].name=B
property[1].value=2
property[2].name=C
property[2].value=3
Output:
result.toString() = [
ReportParam{name='A', value='1'},
ReportParam{name='B', value='2'},
ReportParam{name='C', value='3'}]
Process finished with exit code 0
I know it's bit late of an answer, but if I understand your problem statement correctly, you can use :
#ConfigurationProperties
to get your job done.
Here is my spring-boot example with a YAML file for the sake of convenience (same can be achieved through properties file as well).
application.yaml:
xyz:
xyzprops :
-
name: cbc
value: 441
-
name: obc
value: 443
XYZ class:
#Component
#ConfigurationProperties(prefix = "xyz")
public class XYZ{
private List<XYZProps> xyzprops;
public List<XYZProps> getXyzprops() {
return xyzprops;
}
public void setXyzprops(List<XYZProps> xyzprops) {
this.xyzprops = xyzprops;
}
public class XYZProps{
String name;
String value;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
And then #Autowire XYZ where you want to use it.
I would use JSON:
in your file:
property=[{"name":"A","value":"1"},{"name":"B","value":"2"},{"name":"C","value":"3"}]
and then deserialize it using com.google.gson.gson (or any other) library:
ArrayList<XYZ> propertiesList;
propertiesList = new gsonbuilder().create().fromjson(property, propertiesList.class);
NOTE: I haven't tested this code, and i'm not very familiar with java so i am sure there is a better,cleaner way to implement this.

How can I use BeanUtils converter to List?

Here, the request Param string is :
firstName=jack&lastName=lily&gender=1&foods=Steak&foods=Pizza&quote=Enter+your+favorite+quote!&education=Jr.High&tOfD=Day
And Mapped class is :
public class Student {
private String firstName;
private String lastName;
private Integer gender;
private List<String> foods;
private String quote;
private String education;
private String tOfD;
getXxx()....;
setXxx()....;
}
And Now, I want to write a generic util class to convert the string to a bean.
public final class InjectUtil<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(InjectUtil.class);
public static <T> T converter2Obj(String source, Class<T> tClass) {
T t = null;
try {
t = tClass.newInstance();
if(source != null && source.length() > 0) {
String[] fields = source.split("&");
for(String field : fields) {
String[] fieldKeyValue = field.split("\\=");
String fieldKey = fieldKeyValue[0];
String fieldValue = fieldKeyValue[1];
// help
}
}
} catch(InstantiationException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
return t;
}
}
take care of the help, how can i use BeanUtils converter the "foods=Steak&foods=Pizza" to the List attribute.
Here is a way of doing it :
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.beanutils.BeanUtils;
public final class InjectUtil<T> {
// private static final Logger LOGGER =
// LoggerFactory.getLogger(InjectUtil.class);
public static <T> T converter2Obj(String source, Class<T> tClass) {
T t = null;
try {
t = tClass.newInstance();
Map<String, Object> params = new HashMap<String, Object>();
if (source != null && source.length() > 0) {
String[] fields = source.split("&");
for (String field : fields) {
String[] fieldKeyValue = field.split("\\=");
String fieldKey = fieldKeyValue[0];
String fieldValue = fieldKeyValue[1];
if (params.containsKey(fieldKey)) {
//the key does not exist as yet
Object keyValueRetrieved = params.get(fieldKey);
if (keyValueRetrieved instanceof String) {
//key exists , it is single value that has been added now.
//change that now to a list to add more values
ArrayList<String> values = new ArrayList<String>();
values.add(keyValueRetrieved.toString());
values.add(fieldValue);
params.put(fieldKey, values);
} else {
//key exists , it is a list already. Add more values
((ArrayList<String>) keyValueRetrieved).add(fieldValue);
}
} else {
params.put(fieldKey, fieldValue);
}
}
}
BeanUtils.populate(t, params);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("--------------------------------------------");
System.out.println(t.toString());
System.out.println("--------------------------------------------");
return t;
}
}
There are optimizations you can do , hopefully this gives you an idea
You can use as follows, you need to populate foodList (with values as [Steak,Pizza]) and then set to the bean using following method.
PropertyUtils.setProperty(studentBean, "foods", foodList);
or
BeanUtils.setProperty(studentBean, "foods", foodList);

Java Hibernate Transformer AliasToBeanNestedResultTransformer

i have a query like this. i pass the student ID i need some fields from Student as well as their parent as well some fields from the parent->Address[here is the main problem i am facing] i am using AliasToBeanNestedResultTransformer transformer by Sami Andoni
here is the implementation of it CODE
here is my code.
public List<Student>searchForStudent(Integer studentId)
{
Projection p=Projections.projectionList().create()
.add(Projections.property("name"),"name")//the student name it works O.K
.add(Projections.property("lastname"),"lastname")//the student name it works O.K
.add(Projections.property("age"),"age")//the student AGE it works O.K
.add(Projections.property("p.phone"),"parent.phone")//the parent phone it works O.K
.add(Projections.property("address.state").as("parent.Address.state")); // i need a field from address.state here is the problem...
Session session = ......
Criteria like = session.createCriteria(Student.class).add(prepareForSelect())//some filters..
.createAlias("parent","p")//the parent of the student. a student have one parent
.createAlias("parent.Address","address")//the address of the parent.... a parent have one address.
.setProjection(p)
.setResultTransformer(new AliasToBeanNestedResultTransformer(Student.class));
List<Student>results=like.list();
return results;
}
it throws
Exception in thread "main" org.hibernate.PropertyAccessException: IllegalArgumentException occurred while calling setter of com.generic.model.Parent.Address
FYI is some type mismatch i have done some tracing in SAMI code and i see this
[MyState]
[Address]
seems that Hibernate is returning a String State MyState in this case and the transformer is using a Address Object and this is the type Mismatch.
is any help is hugely needed it
thanks a lot.
I have improved the SamiAndoni class, maybe it solve your issue
package com.alutiiq.develop.promanagesys.core.util;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.HibernateException;
import org.hibernate.property.PropertyAccessor;
import org.hibernate.property.PropertyAccessorFactory;
import org.hibernate.property.Setter;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.hibernate.transform.ResultTransformer;
/**
* Help to transform alises with nested alises
*
* #author Miguel Resendiz
*
*/
public class AliasToBeanNestedResultTransformer extends
AliasedTupleSubsetResultTransformer {
private static final long serialVersionUID = -8047276133980128266L;
private static final int TUPE_INDEX = 0;
private static final int ALISES_INDEX = 1;
private static final int FIELDNAME_INDEX = 2;
private static final PropertyAccessor accessor = PropertyAccessorFactory
.getPropertyAccessor("property");
private final Class<?> resultClass;
private Object[] entityTuples;
private String[] entityAliases;
private Map<String, Class<?>> fieldToClass = new HashMap<String, Class<?>>();
private Map<String, List<?>> subEntities = new HashMap<String, List<?>>();
private List<String> nestedAliases = new ArrayList<String>();
private Map<String, Class<?>> listFields = new HashMap<String, Class<?>>();
public boolean isTransformedValueATupleElement(String[] aliases,
int tupleLength) {
return false;
}
public AliasToBeanNestedResultTransformer(Class<?> resultClass) {
this.resultClass = resultClass;
}
public Object transformTuple(Object[] tuple, String[] aliases) {
handleSubEntities(tuple, aliases);
cleanParams(tuple, aliases);
ResultTransformer rootTransformer = new AliasToBeanResultTransformer(
resultClass);
Object root = rootTransformer.transformTuple(entityTuples,
entityAliases);
loadSubEntities(root);
cleanMaps();
return root;
}
private void handleSubEntities(Object[] tuple, String[] aliases)
throws HibernateException {
String fieldName = "";
String aliasName = "";
try {
for (int i = 0; i < aliases.length; i++) {
String alias = aliases[i];
if (alias.contains(".")) {
String[] sp = alias.split("\\.");
StringBuilder aliasBuilder = new StringBuilder();
for (int j = 0; j < sp.length; j++) {
if (j == 0) {
fieldName = sp[j];
} else {
aliasBuilder.append(sp[j]);
aliasBuilder.append(".");
}
}
aliasName = aliasBuilder.substring(0,
aliasBuilder.length() - 1);
nestedAliases.add(alias);
manageEntities(fieldName, aliasName, tuple[i]);
}
}
} catch (NoSuchFieldException e) {
throw new HibernateException("Could not instantiate resultclass: "
+ resultClass.getName() + " for field name: " + fieldName
+ " and alias name:" + aliasName);
}
}
private Class<?> findClass(String fieldName) throws NoSuchFieldException,
SecurityException {
if (fieldToClass.containsKey(fieldName)) {
return fieldToClass.get(fieldName);
} else {
Class<?> subclass = resultClass.getDeclaredField(fieldName)
.getType();
if (subclass.equals(List.class) || subclass.equals(Set.class)) {
if (subclass.equals(List.class)) {
listFields.put(fieldName, LinkedList.class);
} else {
listFields.put(fieldName, HashSet.class);
}
Field field = resultClass.getDeclaredField(fieldName);
ParameterizedType genericType = (ParameterizedType) field
.getGenericType();
subclass = (Class<?>) genericType.getActualTypeArguments()[0];
}
fieldToClass.put(fieldName, subclass);
return subclass;
}
}
#SuppressWarnings("unchecked")
private void manageEntities(String fieldName, String aliasName,
Object tupleValue) throws NoSuchFieldException, SecurityException {
Class<?> subclass = findClass(fieldName);
if (!subEntities.containsKey(fieldName)) {
List<Object> list = new ArrayList<Object>();
list.add(new ArrayList<Object>());
list.add(new ArrayList<String>());
list.add(FIELDNAME_INDEX, subclass);
subEntities.put(fieldName, list);
}
((List<Object>) subEntities.get(fieldName).get(TUPE_INDEX))
.add(tupleValue);
((List<String>) subEntities.get(fieldName).get(ALISES_INDEX))
.add(aliasName);
}
private void cleanParams(Object[] tuple, String[] aliases) {
entityTuples = new Object[aliases.length - nestedAliases.size()];
entityAliases = new String[aliases.length - nestedAliases.size()];
for (int j = 0, i = 0; j < aliases.length; j++) {
if (!nestedAliases.contains(aliases[j])) {
entityTuples[i] = tuple[j];
entityAliases[i] = aliases[j];
++i;
}
}
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private void loadSubEntities(Object root) throws HibernateException {
try {
for (String fieldName : subEntities.keySet()) {
Class<?> subclass = (Class<?>) subEntities.get(fieldName).get(
FIELDNAME_INDEX);
ResultTransformer subclassTransformer = new AliasToBeanNestedResultTransformer(
subclass);
Object subObject = subclassTransformer.transformTuple(
((List<Object>) subEntities.get(fieldName).get(0))
.toArray(),
((List<Object>) subEntities.get(fieldName).get(1))
.toArray(new String[0]));
Setter setter = accessor.getSetter(resultClass, fieldName);
if (listFields.containsKey(fieldName)) {
Class<?> collectionClass = listFields.get(fieldName);
Collection subObjectList = (Collection) collectionClass
.newInstance();
subObjectList.add(subObject);
setter.set(root, subObjectList, null);
} else {
setter.set(root, subObject, null);
}
}
} catch (Exception e) {
throw new HibernateException(e);
}
}
private void cleanMaps() {
fieldToClass = new HashMap<String, Class<?>>();
subEntities = new HashMap<String, List<?>>();
nestedAliases = new ArrayList<String>();
listFields = new HashMap<String, Class<?>>();
}
}
I hope it help you.
--------------edit 07/25/15---------------
To group nested list.
public List<? extends Entity<?>> cleanList(
List<? extends Entity<?>> resultList) throws DataException {
List<Entity<?>> entities = new ArrayList<Entity<?>>();
Entity<?> current = null;
try {
for (Entity<?> entity : resultList) {
if (entity.getId() == null) {
continue;
}
if (current == null) {
current = entity;
continue;
}
if (current.getId().equals(entity.getId())) {
append(current, entity);
} else {
entities.add(current);
current = entity;
}
}
if (current != null) {
entities.add(current);
}
cleanSubList(entities);
return entities;
} catch (Exception e) {
throw new DataException(e);
}
}
#SuppressWarnings({ "rawtypes", "unchecked" })
public Set<? extends Entity<?>> cleanList(
Set<? extends Entity<?>> resultList) throws DataException {
List listToClean = new LinkedList();
listToClean.addAll(resultList);
listToClean = cleanList(listToClean);
resultList.clear();
resultList.addAll(listToClean);
return resultList;
}
#SuppressWarnings({ "unchecked", "rawtypes" })
private void append(Entity<?> current, Entity<?> next)
throws IllegalArgumentException, IllegalAccessException {
Field[] fields = current.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(List.class)) {
field.setAccessible(true);
List valueNext = (List) field.get(next);
List valueCurrent = (List) field.get(current);
if (valueNext != null) {
if (valueCurrent != null) {
valueCurrent.addAll(valueNext);
field.set(current, valueCurrent);
} else {
field.set(current, valueNext);
}
}
} else if (field.getType().equals(Set.class)) {
field.setAccessible(true);
Set valueNext = (Set) field.get(next);
Set valueCurrent = (Set) field.get(current);
if (valueNext != null) {
if (valueCurrent != null) {
valueCurrent.addAll(valueNext);
field.set(current, valueCurrent);
} else {
field.set(current, valueNext);
}
}
}
}
}
#SuppressWarnings({ "rawtypes", "unchecked" })
private void cleanSubList(List<? extends Entity<?>> listToClean)
throws IllegalArgumentException, IllegalAccessException,
DataException {
for (Entity<?> entity : listToClean) {
Field[] fields = entity.getClass().getDeclaredFields();
for (Field field : fields) {
if (field.getType().equals(List.class)) {
field.setAccessible(true);
List valueToClean = (List) field.get(entity);
// Throw a thread
if (valueToClean != null) {
valueToClean = cleanList(valueToClean);
field.set(entity, valueToClean);
}
} else if (field.getType().equals(Set.class)) {
field.setAccessible(true);
Set valueToClean = (Set) field.get(entity);
// Throw a thread
if (valueToClean != null) {
valueToClean = cleanList(valueToClean);
field.set(entity, valueToClean);
}
}
}
}
}
To speed up the process I´m suggesting throw a thread in the main process.
This is my Entity interface:
package com.alutiiq.develop.promanagesys.common.entity;
import java.io.Serializable;
/**
* Entity for Hibernate comunications
*
* #author Miguel Resendiz
*
* #param <I>
* Primary key type
*/
public interface Entity<I extends Serializable> extends Serializable {
/**
* Enable poissibility to write generic queries using primary key
*
* #return primary key value for entity
*/
I getId();
void setId(I id);
void setId(String id);
}
Usage example:
criteria.setResultTransformer(new AliasToBeanNestedResultTransformer(
entityClass));
List<Project> projects = criteria.list();
projects = (List<Project>) cleanList(projects);

Simple-XML: Overriding an element name at runtime

I am using simple-xml to perform XML serialization/deserialization in my Java application. I have a class as follows:
#Root(name="config")
public class Config{
#Element(name="update_interval")
private int updateInterval;
#Element(name="timestamp")
private long timestamp;
//...
//...
}
Now, this would produce XML like the following:
<config>
<update_interval>2000</update_interval>
<timestamp>1234567890</timestamp>
</config>
Question:
How can I override the element name at runtime, so that in some cases, the XML reads as follows?
<config>
<updt_int>2000</updt_int>
<ts>1234567890</ts>
</config>
Edit:
To clarify, I want to override the element names only in some cases. So basically,
if(condition){
//Override Element Names
} else {
//Serialize Normally
}
I found an easy way to achieve serialization in this case, thanks to this comment.
However, I haven't been able to de-serialize such an XML document. Here's my partial solution:
/*
* Config.java
*/
#Root(name="config", strict = false)
public class Config {
#Element(name="timestamp", required = false)
private long timestamp;
#Element(name = "update_interval", required = false)
private int updateInterval;
public Config() {
}
public int getUpdateInterval() {
return updateInterval;
}
public void setUpdateInterval(int updateInterval) {
this.updateInterval = updateInterval;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
#Override
public String toString() {
return "Config{" +
"timestamp=" + timestamp +
", updateInterval=" + updateInterval +
'}';
}
}
/*
* Custom Visitor implementation
*/
public class MyInterceptor implements Visitor {
private static int sReadCount = 0;
private static int sWriteCount = 0;
#Override
public void read(Type field, NodeMap<InputNode> node) throws Exception {
/*
* This is where I need help!
*
*
* This method is only called once: for the <config> node
* It is not called for the other nodes since they are not "recognized"
* i.e., there are no annotations for the nodes <ts> and <updt_int>
*/
System.out.println("Read Count : "+ (++sReadCount));
System.out.println(node.getName());
System.out.println(node.getNode());
}
#Override
public void write(Type field, NodeMap<OutputNode> node) throws Exception {
/*
* This works like a charm.
*/
System.out.println("Write Count : "+ (++sWriteCount));
OutputNode opNode = node.getNode();
if("timestamp".equals(opNode.getName())){
opNode.setName("ts");
}
if("update_interval".equals(opNode.getName())){
opNode.setName("updt_int");
}
}
}
/*
*
*/ Main class
public class Bootstrap {
static final Random RANDOM = new Random();
public static void main(String [] args){
Config cfg = new Config();
cfg.setTimestamp(RANDOM.nextLong());
cfg.setUpdateInterval(1000);
Serializer serializer = new Persister(new VisitorStrategy(new MyInterceptor()));
StringWriter writer = new StringWriter();
try {
serializer.write(cfg, writer);
} catch (Exception e) {
e.printStackTrace();
}
String serialized = writer.toString();
System.out.println(serialized);
Config desCfg = null;
try {
desCfg = serializer.read(Config.class, serialized);
} catch (Exception e) {
e.printStackTrace();
}
if(desCfg != null){
System.out.println(desCfg.toString());
}
}
}

Categories