将嵌套对象编组为“平面”XML 结构

Edw*_*son 5 java xml jaxb

我如何编组对象层次结构,以便组件对象不成为嵌套的 XML 元素,而是使它们的属性成为根元素的直接子元素,其名称以类型为前缀。

例如,给定:

                                                    (A)
public class Customer {

    protected String firstName;
    protected String lastName;
    protected Address address;
}

public class Address {

    protected String street;
    protected String city;
}
Run Code Online (Sandbox Code Playgroud)

使用通常的 JAXB 注释会导致

                                                    (B)
<customer>
    <firstName>Juan</firstName>
    <lastName>dela Cruz</lastName>
    <address>
        <street>123 Rizal Avenue</street>
        <city>Manila</city>
    </address>
</customer>
Run Code Online (Sandbox Code Playgroud)

但是,相反,我需要编组相同的

                                                    (C)
<customer>
    <firstName>Juan</firstName>
    <lastName>dela Cruz</lastName>
    <address_street>123 Rizal Avenue</address_street>
    <address_city>Manila</address_city>
</customer>
Run Code Online (Sandbox Code Playgroud)

如果有一些 JAXB 咒语来解决我的需求,那就太好了,因为我已经使用 JAXB 来解决这个问题的大部分问题。事实上,这些对我的具体情况提出了一些限制:

  1. (A)中的 Java 类由 JAXB 根​​据与(B)中的 XML 结构相对应的现有模式生成。我不想维护生成的类的修改版本。
  2. 我不拥有或维护上述架构。实际的模式相当大,并且经常需要进行微小的修改。提出并维护等效的模式将是乏味的。另外,为了跟上架构修改,我依赖 JAXB 的自动类生成。
  3. 如果可以让事情变得更容易,嵌套最多只能深一层。在示例中,Address不会包含任何其他复杂类型。

我正在查看@XmlPathMOXy 的注释,但我不知道如何获取(C)中的节点名称前缀。

我梦想有一个解决方案,其中一些提供正确 JAXB 注释的 xjc 自定义可以让我继续前进,但从我目前的搜索来看,这似乎不太可能。任何非 JAXB 解决方案就足够了。

Edw*_*son 0

我使用XStream Converter解决了这个问题。它检查注释@XmlType以确定是否正在转换 JAXB bean。所有其他类型都通过默认转换器。

虽然以 JAXB 为中心的解决方案会很好,但 XStream 提供了一个非常简单的解决方案。

public class FlatXmlConverter implements Converter {

    private static final Logger log =
            LoggerFactory.getLogger(NvpConverter.class);

    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer,
            MarshallingContext context) {
        Class<? extends Object> sourceClass = source.getClass();
        String prefix = (String) context.get("prefix");
        for (Field field : sourceClass.getDeclaredFields()) {
            if (!field.isAccessible()) {
                field.setAccessible(true);
            }
            String name = field.getName();
            Class<?> type = field.getType();

            try {
                Object value = field.get(source);
                if (value != null) {
                    if (type.isAnnotationPresent(XmlType.class)) {
                        context.put("prefix", name);
                        context.convertAnother(value);
                        context.put("prefix", null);
                    } else {
                        String nodeName;
                        if (prefix == null) {
                            nodeName = name;
                        } else {
                            nodeName = prefix + "_" + name;
                        }

                        writer.startNode(nodeName);
                        context.convertAnother(value);
                        writer.endNode();
                    }
                }
            } catch (IllegalArgumentException ex) {
                log.error("IllegalArgumentException", ex);
            } catch (IllegalAccessException ex) {
                log.error("IllegalAccessException", ex);
            }
        }
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    @SuppressWarnings({"rawtypes", "unchecked"})
    public boolean canConvert(Class type) {
        log.debug("canConvert({})", type);
        return type.isAnnotationPresent(XmlType.class);
    }
}
Run Code Online (Sandbox Code Playgroud)