5

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

例如,给定:

                                                    (A)
public class Customer {

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

public class Address {

    protected String street;
    protected String city;
}

使用通常的 JAXB 注释会导致

                                                    (B)
<customer>
    <firstName>Juan</firstName>
    <lastName>dela Cruz</lastName>
    <address>
        <street>123 Rizal Avenue</street>
        <city>Manila</city>
    </address>
</customer>

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

                                                    (C)
<customer>
    <firstName>Juan</firstName>
    <lastName>dela Cruz</lastName>
    <address_street>123 Rizal Avenue</address_street>
    <address_city>Manila</address_city>
</customer>

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

  1. (A)中的 Java 类由 JAXB 从与(B)中的 XML 结构对应的现有模式生成。我不希望维护生成的类的修改版本。
  2. 我不拥有或维护上述架构。实际的模式非常大,并且经常会进行细微的修改。想出和维护一个等效的模式是很乏味的。此外,为了跟上模式修改,我依赖 JAXB 自动生成类。
  3. 如果它使事情变得更容易,嵌套最多只有一层。在示例中,Address不会包含任何其他复杂类型。

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

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

4

6 回答 6

2

注意: 我是EclipseLink JAXB (MOXy)负责人,也是JAXB (JSR-222)专家组的成员。

您可以使用 EclipseLink JAXB (MOXy) 中的外部映射文件将第二个映射应用到您的对象模型,并使用@XmlPath扩展来展平对象模型。

外部映射文件 (oxm.xml)

如果您的模型类位于名为forum11007814.

<?xml version="1.0"?>
<xml-bindings
    xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
    package-name="forum11007814"
    xml-accessor-type="FIELD">
    <java-types>
        <java-type name="Customer">
            <xml-root-element/>
            <java-attributes>
                <xml-element java-attribute="address" xml-path="."/>
            </java-attributes>
        </java-type>
        <java-type name="Address">
            <java-attributes>
                <xml-element java-attribute="street" name="address_street"/>
                <xml-element java-attribute="city" name="address_city"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

演示

下面是演示如何在创建JAXBContext.

package forum11007814;

import java.io.File;
import java.util.*;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.JAXBContextFactory;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String,Object>(1);
        properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11007814/oxm.xml");
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);

        File xml = new File("src/forum11007814/c.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Customer customer = (Customer) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(customer, System.out);
    }

}

jaxb.properties

要将 MOXy 指定为您的 JAXB 提供程序,您需要添加一个jaxb.properties在与您的域模型相同的包中调用的文件,其中包含以下条目。

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

c.xml/输出

<?xml version="1.0" encoding="UTF-8"?>
<customer>
   <firstName>Juan</firstName>
   <lastName>dela Cruz</lastName>
   <address_street>123 Rizal Avenue</address_street>
   <address_city>Manila</address_city>
</customer>
于 2012-06-13T08:56:16.377 回答
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);
    }
}
于 2012-06-14T07:18:23.157 回答
0

我很确定没有标准的方法可以做到这一点。您可以做的是创建两个 java 类,一个具有层次结构,一个具有平面结构,并使用 java introspection 将数据从一个复制到另一个,然后使用第二个创建 xml。

于 2012-06-13T03:58:06.730 回答
0

注意:我是 juffrou-xml 的创建者,它使您能够以一种简单的方式做到这一点:只需在 xml-mapping 文件中配置它:

<root-element xml="Customer" type="org.yourpackage.Customer">
  <element property="firstName" />
  <element property="lastName" />
  <element property="address.street" xml="address_street"/>
  <element property="address.city" xml="address_city"/>
</root-element>

<root-element xml="Address" type="org.yourpackage.Address" />

通过这种方式,将嵌套的 bean 编组为扁平的 XML 结构很容易。反过来——解组——将按预期创建对象图。

在这里查看 Juffrou-XML 。

于 2013-05-21T22:26:39.993 回答
-1

您是否检查过 JiBX(将 XML 数据绑定到 Java 对象的工具)? http://jibx.sourceforge.net/binding/tutorial/binding-structures.html

于 2012-06-13T06:44:33.960 回答
-1

使用@XmlID 和@XmlIDREF。并使用所有对象的列表创建类。

于 2012-06-14T07:57:53.077 回答