动态创建POJO / bean和使用CGLib

时间:2019-05-22 16:43:43

标签: java reflection javabeans pojo

我需要解析一个文本文件并生成JSON文档。文本文件具有文本模式,其中包含一个键,该键是名称,值是带有标题的TSV巨大文本。

我可以解析文本文件并使用标题生成Bean类,现在我想将数据设置为此生成的Bean类。我正在使用反射来做到这一点。

Class<?> beanClass = BeanClassGenerator.beanGenerator(k, mapForBeanGeneration);
            try {
                Object beanClassObject = beanClass.newInstance();
                lines.forEach(line -> {
                    if (line != null && !line.isEmpty() && !line.equals("null")) {
                        String[] lineData = line.split("\t");
                        System.out.println("LineData length :: " + lineData.length);
                        Method[] methods = beanClass.getMethods();
                        System.out.println("Methods length :: " + methods.length);
                        int index = 0;
                        for (Method m : methods) {
                            m.setAccessible(true);
                            if (m.getName().startsWith("set")) {
                                try {
                                    if ((lineData.length <= index) && lineData[index] != null) {
                                        m.invoke(beanClassObject, lineData[index]);
                                        index++;
                                    } else {
                                        m.invoke(beanClassObject, " ");
                                    }
                                } catch (IllegalAccessException | InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                });
                ObjectMapper om = new ObjectMapper();
                System.out.println(om.writeValueAsString(beanClassObject));
            } catch (InstantiationException | IllegalAccessException | JsonProcessingException e) {
                e.printStackTrace();
  }});

该方法的问题在于,大多数情况下所有列值可能都没有数据,因此可以将其清空。

我想知道是否有更简单的方法可以做到这一点。任何帮助表示赞赏。

这是bean的生成方法。

public static Class<?> beanGenerator(final String className, final Map<String, Class<?>> properties) {
        BeanGenerator beanGenerator = new BeanGenerator();
        beanGenerator.setNamingPolicy(new NamingPolicy() {
            @Override
            public String getClassName(String prefix, String source, Object key, Predicate names) {
                return className;
            }
        });

        BeanGenerator.addProperties(beanGenerator, properties);
        return (Class<?>) beanGenerator.createClass();
    }

这是示例文本文件,需要将其转换为JSON输出。

<Data1>
Col1  col2 col3 col4 col5
even    sense   met has
root    greatest    spin    mostly
gentle  held    introduced  palace
cold    equator remember    grandmother
slightly    butter  depth   like
distant second  coast   everyone


<Data2>
Col1  col2 col3 col4 col5 col6 col7 col8
greatest    rope    operation   flies   brown   continent   combination read
slightly    diagram he  grandfather where   party   fifty   pour
well    put plastic anyway  refer   careful correct furniture
how since   army    tongue  birthday    been    clock   official
table   command specific    distant cutting hill    movie   experience
national    though  stopped youth   army    underline   five    know

<Data3>
Col1 col2 col3 col4 col5 col6 col7 col8 col9 col9 col10
vessels characteristic  ship    joy than    tomorrow    high    seven   future  trade
try gray    fourth  advice  week    stream  motion  musical whom    tin
limited daughter    large   rice    came    home    chicken wheat   engine  box
easy    city    pair    strange stage   visitor coach   announced   allow   simple
jet therefore   single  during  construction    flag    bigger  muscle  complex pleasure
income  several coat    range   dull    cattle  damage  jump    present shake

JSON输出:

[{
    "<Data1>": [{
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": ""
        }
    ]

}, {
    "<Data2>": [{
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": "",
            "col5": "",
            "col6": "",
            "col7": "",
            "col8": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": "",
            "col5": "",
            "col6": "",
            "col7": "",
            "col8": ""
        },
        {
            "col1": "",
            "col2": "",
            "col3": "",
            "col4": "",
            "col5": "",
            "col6": "",
            "col7": "",
            "col8": ""
        }
    ]

}]

我想出了使用地图的解决方案。

Map<String, List<Map<String, String>>> finalMap = new HashMap<>();
        metadataMap.forEach((k, v) -> {

            List<Map<String, String>> datamap = new ArrayList<>();

            String key = k;
            String[] fields = v.getFields();
            List<String> lines = v.getLines();


            lines.forEach(line -> {
                if (line != null && !line.isEmpty() && !line.equals("null")) {
                    String[] fieldData = line.split("\t");
                    Map<String, String> eachLineMap = new HashMap<>();
                    for (int index = 0; index < fields.length; index++) {
                        if (index < fieldData.length && (fieldData[index] != null && !fieldData[index].isEmpty())) {
                            eachLineMap.put(fields[index], fieldData[index]);
                        } else {
                            eachLineMap.put(fields[index], " ");
                        }
                        datamap.add(eachLineMap);
                    }
                }
            });
            finalMap.put(key, datamap);
        });

        try {
            output = new ObjectMapper().writeValueAsString(finalMap);
        }catch(JsonProcessingException e){
            e.printStackTrace();
        }

3 个答案:

答案 0 :(得分:1)

您不需要编写所有逻辑,只需使用Apache Commons BeanUtils;它提供了一种实用程序方法(在 MANY 其他实用程序中),该方法采用Map字段名称和字段值并用它填充给定的bean:

    BeanUtils.populate(target, fieldNameValueMap);

然后,您唯一需要实现的就是创建fieldNameValueMap Map的逻辑;您可以使用以下简单方法进行操作:

    Map<String, String> createFieldNameValueMap(String headerLine, String valuesLine) {
        String[] fieldNames = headerLine.split("\t");
        String[] fieldValues = valuesLine.split("\t");

        return IntStream.range(0, fieldNames.length)
            .mapToObj(Integer::new)
            .collect(Collectors.toMap(idx -> fieldNames[idx], idx -> fieldValues[idx]));
    }

您可以使用以下工作示例测试此解决方案:

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import org.apache.commons.beanutils.BeanUtils;

import lombok.Data;

public class DynamicBeanUtils {

    static Map<String, String> createFieldNameValueMap(String headerLine, String valuesLine) {
        String[] fieldNames = headerLine.split("\t");
        String[] fieldValues = valuesLine.split("\t");

        return IntStream.range(0, fieldNames.length)
            .mapToObj(Integer::new)
            .collect(Collectors.toMap(idx -> fieldNames[idx], idx -> fieldValues[idx]));
    }

    public static void main(String[] args) {
        String headerLine = "booleanValue\tintValue\tstringValue\tdoubleValue\totherValue";
        String valuesLine = "true\t12\tthis bean will be populated\t22.44\ttest string!!!";

        Object target = new MyBean();
        try {
            BeanUtils.populate(target, createFieldNameValueMap(headerLine, valuesLine));
        } catch (IllegalAccessException | InvocationTargetException e) {
            // HANDLE EXCEPTIONS!
        }

        System.out.println(target);
    }

    @Data
    public static class MyBean {
        private String stringValue;
        private double doubleValue;
        private int intValue;
        private boolean booleanValue;
        private String otherValue;
    }
}

这是此依赖项的Maven存储库页面,因此您可以将其包含在构建中:https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils/1.9.3

我在此解决方案中也使用了Lombok,只是免去了编写getter / setters / toString来测试该解决方案的麻烦;但这不是您的解决方案所必需的。

希望这会有所帮助。

答案 1 :(得分:1)

您将彻底解决您的问题。
您的数据以可变长度数组的形式组织; 并不需要一些疯狂的即时生成类解决方案。 作为旁注, 即时生成类并不是天生的疯狂。 在这种情况下使用即时生成类非常疯狂。

执行以下操作:

  1. 查看您的数据; 它的组织如下:
    1. 第一:外键
    2. second:恰好一行包含可变数量的以空格分隔的内部键数组。
    3. 第三名:包含值的行数。
  2. 设计解决方案以解决您的问题
    1. 读取外键。 使用该值创建JSON的外键部分。
    2. 读取内部键。 将它们存储在数组中; 使用LinkedList, 不是ClownList(ArrayList)。
    3. 执行此操作,直到下一个空行:
      1. 读取一行值。
      2. 编写内部JSON;使用内键作为此键。
    4. 跳过空行,直到出现以下情况之一:
      1. 如果在文件末尾,请写入JSON的结尾部分。
      2. 如果您阅读下一个外键,请转到上面的第2行(读取内键)。
  3. 编写代码。

答案 2 :(得分:0)

我意识到,与其使用复杂的方法来创建POJO,不如说是。最好使用Map,然后使用Jackson ObjectMapper将它们转换为JSON。向其他认为这可能是一种有用方法的人发布。

public String convert(Map<String, ? extends Metadata> metadataMap) {
        String output = "";
        Map<String, List<Map<String, String>>> finalMap = new HashMap<>();
        metadataMap.forEach((k, v) -> {

            List<Map<String, String>> datamap = new LinkedList<>();

            String key = k;
            String[] fields = v.getFields();
            List<String> lines = v.getLines();


            lines.forEach(line -> {
                if (line != null && !line.isEmpty() && !line.equals("null")) {
                    String[] fieldData = line.split("\t",-1);
                    Map<String, String> eachLineMap = new HashMap<>();
                    for (int index = 0; index < fields.length; index++) {
                        if (index < fieldData.length && (fieldData[index] != null && !fieldData[index].isEmpty())) {
                            eachLineMap.put(fields[index], fieldData[index]);
                        } else {
                            eachLineMap.put(fields[index], " ");
                        }
                        datamap.add(eachLineMap);
                    }
                }
            });
            finalMap.put(key, datamap);
        });

        try {
            output = new ObjectMapper().writeValueAsString(finalMap);
        }catch(JsonProcessingException e){
            e.printStackTrace();
        }

        return output;
    }