一种在运行时查找包含列表的另一个对象的所有子对象的方法

时间:2015-04-08 05:32:08

标签: java xml list generics jaxb

我有一个项目,它基于XML架构,可能会发生变化。使用JAXB将架构转换为Java。模式的几个部分保证保持不变;特别是,用户必须填写的表单的基本元素。

我将基于此(主题变更)JAXB生成的代码生成表单。问题是因为我的代码是常量,但架构可以随时更改,我不能在任何JAXB代码中硬编码,除了基本元素。

因此,我必须递归地找到基础对象下面的所有元素。这不是太麻烦,只需要一点反射魔法(找到所有对象的'getter'方法的返回类型,重复)。真正的问题是我遇到一个列表。因为列表在运行时丢失了类型,所以我无法确定列表包含的内容,也无法进一步递归。

有没有人有一个出色的解决方案来解决这个问题?我已经调查了大多数其他方式在运行时保留列表类型,但没有一个工作。我很清楚这可能是不可能的,但我想在放弃之前探索所有选择。如果还有其他办法,我愿意尝试。

我目前的代码:

public static void getObjectDescendants(Object obj, int indent) throws Exception {
    // Handle list of objects.
    if (obj instanceof List) {
        for(int i=0;i<indent;++i){System.out.print("  ");}
        System.out.println("List");
        Class<?> clazz = getListType((List<?>) obj); // Get type of object within list

        // Create new object of that type from constructor. (temp solution)
        Object object = clazz.getConstructor().newInstance();

        getObjectDescendants(object, indent+1);
        return;
    }

    // Handle regular object.
    for (MethodDescriptor pd : Introspector.getBeanInfo(obj.getClass()).getMethodDescriptors()) {
        // For each method this object has...
        Method method = pd.getMethod();

        // If it does not return a complex object, or a list, we don't care about it.
        if (!method.getReturnType().toString().contains("JAXB.generated.object")
                && !method.getReturnType().toString().contains("java.util.List")) {
            continue;
        }

        // Print out indent, and name of method.
        for(int i=0;i<indent;++i){System.out.print("  ");}
        System.out.println(method.getDeclaringClass().getSimpleName() + "." + method.getName() + "()");

        Object object;
        try {
            if (method.getReturnType().equals(List.class)) {
                object = method.invoke(obj);
            } else {
                Constructor<?> constructor = method.getReturnType().getConstructor(String.class);
                object = constructor.newInstance();
            }
        } catch (Exception e) {
            continue; // Couldn't seem to handle this one.
        }

        getObjectDescendants(object, indent+1);
    }
}

private static Class<?> getListType(final List<?> list) {
    // Find out what object type is in the list, and return it.
    Class<?> clazz = list.get(0).getClass(); // Doesn't work, since list is always empty.
    return clazz;
}

1 个答案:

答案 0 :(得分:1)

您可以使用JAXB模型而不是反射到JAXB类中。从类或包创建JAXB上下文时,它会创建这些类及其属性的基础模型。在运行时 - 基于注释。在编译期间(XJC) - 基于XML模式。

这是特定于实现的,所以我会把JAXB RI比MOXy更好。

  • 使用JAXB RI创建JAXBContext时,最后会得到JAXBContextImpl的实例。
  • 这有一个方法getTypeInfoSet()返回RuntimeTypeInfoSet
  • 的实例
  • 这个RuntimeTypeInfoSet是JAXB类,属性的完整和完整模型 - 您需要进行编组和解组。
  • 对于复杂类型,您将拥有具有属性(RuntimeClassInfo
  • RuntimePropertyInfo个实例

您可以分析这些结构,在运行时构建您的UI。它比挖掘反射容易得多。基本上这个Runtime...的东西会完成所有必要的反射,所以你不必这样做。

顺便说一下,我个人不会在这里重新发明轮子,而是使用现有的模型驱动的UI生成方法之一,例如Metawidget。您必须将框架连接到JAXB模型,但它似乎比从头开始完成整个过程更容易。

大约7到8年前,我已经基于JAXB实现了XML Schema驱动的UI生成,这是一次冒险。有很多警告。