0

Is there a tool or library that converts object serialized into YAML or XML file into Java code creating that object?

From technical point of view, I see no difficulties here. Both Yaml and XStream (or other similar tools) needs to find Java class name via reflection, find constructor, invoke it, then find setters and invoke them. Reflection invokes can be replaced by Java code generation that is doing the same. It requires a bit work, however.

Does such tool exist? Or maybe someone is developing it and want to share it with public?

P.S. Any library with ability to convert given Java object into Java code would fullfill my needs.

clarification

I expect the tool to convert example YAML:

--- !pl.example.Contact
name: Nathan Sweet
age: 28

Or XML:

<contact>
  <name>Nathan Sweet</name>
  <age>28</age>
</contact>

into the code:

pl.example.Contact contact = new pl.example.Contact();
contact.setName("Nathan Sweet");
contact.setAge(28);
Danubian Sailor
  • 1
  • 38
  • 145
  • 223

3 Answers3

1

I've asked about converting YAML or XML into Java code, but that what I really needed was to generate Java code that would create given Java object. Reading existing YAML can be done by one of many Java YAML libraries.

However, I wasn't able to find any library that would create Java code from given Java object. Maybe I was searching for wrong keywords, maybe I've finished my search too early, but I haven't found anything.

I have written that tool myself. The code is quite primitive and probably will not be enough in many cases (it has no support for abstract classes and interfaces etc.), but was good enough for the task I wanted to accomplish. So I post the source code of my solution as the answer to my question:

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringEscapeUtils;

public class BeanCodeGenerator {

    private class Context {
        private int index;
        private StringBuilder sb = new StringBuilder();
        private String br = "\n\t\t";
    }

    public String generateBeanMethod(Object bean, String methodName) {
        Context ctx = new Context();
        Class type = _getBeanClass(bean);
        ctx.sb.append("\tpublic ").append(type.getCanonicalName()).append(" ").append(methodName)
            .append("() {").append(ctx.br);

        String vname = "null";
        try {
            vname = _valueStr(bean, type, ctx);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        ctx.sb.append("return ").append(vname).append(";\n");
        ctx.sb.append("\t}\n");
        return ctx.sb.toString();
    }

    private String _outputBean(Object bean, Context ctx) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        // output block of code creating bean
        String vname = "v" + ctx.index;
        String cname = bean.getClass().getCanonicalName();
        ctx.sb.append(cname).append(" ").append(vname).append(" = new ").append(cname).append("();").append(ctx.br);
        PropertyDescriptor[] props = PropertyUtils.getPropertyDescriptors(bean);
        for (PropertyDescriptor prop : props) {
            if (prop.getReadMethod() == null || prop.getWriteMethod() == null)
                continue; // skip such 'properties'
            Object value = prop.getReadMethod().invoke(bean);
            if (value == null)
                continue;
            String valueStr = _valueStr(value, prop.getReadMethod().getReturnType(), ctx);
            if (valueStr != null) {
                ctx.sb.append(vname).append(".").append(prop.getWriteMethod().getName()).append("(")
                    .append(valueStr).append(");").append(ctx.br);
            }
        }
        return vname;
    }

    private Class _getBeanClass(Object bean) {
        if (bean == null)
            return null;
       if (_isCollection(bean)) {
           if (bean instanceof List)
               return ArrayList.class;
           if (bean instanceof Set)
               return HashSet.class;
           if (bean instanceof Map)
               return HashMap.class;
       }
       return bean.getClass();
    }

    private String _valueStr(Object value, Class type, Context ctx) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String valueStr = _outputValue(value, type);
        if (valueStr == null) {
            if (_isBean(value)) {
                ctx.index++;
                valueStr = _outputBean(value, ctx);
            } else if (_isCollection(value)) {
                ctx.index++;
                valueStr = _outputCollection(value, ctx);
            }
        }
        if (valueStr == null)
            valueStr = "null";
        return valueStr;
    }

    private String _outputCollection(Object collection, Context ctx) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String vname = "v" + ctx.index;
        if (collection instanceof List) {
            ctx.sb.append("java.util.ArrayList ").append(vname).append(" = new java.util.ArrayList();").append(ctx.br);
            List list = (List) collection;
            for (Object value : list) {
                if (value == null) {
                    ctx.sb.append(vname).append(".add(null);").append(ctx.br);
                    continue;
                }
                String valueStr = _valueStr(value, value.getClass(), ctx);;
                ctx.sb.append(vname).append(".add(").append(valueStr).append(");").append(ctx.br);
            }
        } else if (collection instanceof Set) {
            ctx.sb.append("java.util.HashSet ").append(vname).append(" = new java.util.HashSet();").append(ctx.br);
            Set set = (Set) collection;
            for (Object value : set) {
                if (value == null) {
                    ctx.sb.append(vname).append(".add(null);").append(ctx.br);
                    continue;
                }
                String valueStr = _valueStr(value, value.getClass(), ctx);;
                ctx.sb.append(vname).append(".add(").append(valueStr).append(");").append(ctx.br);
            }
        } else if (collection instanceof Map) {
            ctx.sb.append("java.util.HashMap ").append(vname).append(" = new java.util.HashMap();").append(ctx.br);
            Map map = (Map) collection;
            for (Object item : map.entrySet()) {
                Map.Entry entry = (Map.Entry) item;
                String valueStr = "null";
                String keyStr = "null";
                if (entry.getKey() != null) {
                    keyStr = _valueStr(entry.getKey(), entry.getKey().getClass(), ctx);
                }
                if (entry.getValue() != null) {
                    valueStr = _valueStr(entry.getValue(), entry.getValue().getClass(), ctx);
                }
                ctx.sb.append(vname).append(".put(").append(keyStr).append(", ").append(valueStr).append(");").append(ctx.br);
            }
        }
        return vname;
    }

    private boolean _isBean(Object value) throws SecurityException {
        if (value == null)
            return false;
        if (_isCollection(value))
            return false;
        Class type = value.getClass();
        try {
            type.getConstructor();
            return true;
        } catch (NoSuchMethodException e) {
            return false;
        }
    }

    private boolean _isCollection(Object value) {
        if (value == null)
            return false;
        Class type = value.getClass();
        return List.class.isAssignableFrom(type) || Set.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type); 
    }

    // output simple value, returns null for non-singleliners
    private String _outputValue(Object value, Class type) {
        if (value == null)
            return null;
        Class vtype = value.getClass();
        if (type == long.class) {
            return value.toString() + "L";
        }
        if (type == float.class) {
            return value.toString() + "F";
        }
        if (type == short.class) {
            return "(short) " + value.toString() + "";
        }
        if (type.isPrimitive()) {
            return value.toString();
        }
        if (String.class.isAssignableFrom(type)) {
            return '"' + StringEscapeUtils.escapeJava(value.toString()) + '"';
        }
        if (Integer.class == type || Double.class == type || Boolean.class == type) {
            return type.getName() + ".valueOf(" + value.toString() + ")";
        }
        if (Long.class == type) {
            return type.getName() + ".valueOf(" + value.toString() + "L)";
        }
        if (Short.class == type) {
            return type.getName() + ".valueOf((short)" + value.toString() + ")";
        }
        if (Float.class == type) {
            return type.getName() + ".valueOf(" + value.toString() + "F)";
        }
        if (Date.class.isAssignableFrom(type)) {
            Date date = (Date) value;
            return "new java.util.Date(" + date.getTime() + "L)";
        }
        return null;
    }

}
Danubian Sailor
  • 1
  • 38
  • 145
  • 223
  • Looks to me like a typical "Enterprise" problem :-P. I was pitching, in my company, not to use YAML or XML for test data - unfortunately without hearing. Greetings from 2016! – Chris Suszyński Jun 02 '16 at 19:28
0

You can check the YamlBeans library here.

It can read and write beans in Yaml format.

Here is a sample from their doc:

name: Nathan Sweet
age: 28

public class Contact {
        public String name;
        public int age;
}

YamlReader reader = new YamlReader(new FileReader("contact.yml")); 
Contact contact = reader.read(Contact.class);
System.out.println(contact.age);

Besides, there seems to be Yaml support for JAXB

Edit:

From your clarification, no I don't know any lib that do this out of the box, but it should be not that hard to do it (depending on how complex are the objects you want to load).

Alex
  • 25,147
  • 6
  • 59
  • 55
  • According to the informations on the project site, it is another yaml library, that can serialize Java objects to YAML, but lacks the ability to convert object into Java code... – Danubian Sailor Jun 13 '12 at 11:45
  • What do you mean by `convert object into Java code` ? I thougt you meant serialize/deserialize. – Alex Jun 13 '12 at 11:58
  • OP doesn't want a reader. He wants the YAML instance converted into an "initial value". – Ira Baxter Jun 15 '12 at 10:29
  • It wasn't clear at the time I answered. As I said in the edit, I don't know a lib that can do it out of the box, but it should not be that hard to implement. – Alex Jun 15 '12 at 10:49
0

Yes, you can use swagger hub which can provide you the code (in many languages) for both client and server from YAML. I too use the same, it has capabilities to generate even code for REST APIs.

URL: http://editor.swagger.io/

Tutorials URL: https://swagger.io/tools/open-source/getting-started/