我如何编组一个对象层次结构,使得它们的属性不再是组件对象成为嵌套的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解决了这个问题的大部分问题。事实上,这些对我的具体情况有一些限制:
Address
不包含任何其他复杂类型。我正在查看MOXy中的@XmlPath
注释,但我无法弄清楚如何将节点名称作为(C)中的前缀。
我梦想有一个解决方案,其中一些xjc自定义提供了正确的JAXB注释让我走了,但到目前为止我的搜索看起来不太可能。任何非JAXB解决方案都足够了。
答案 0 :(得分:1)
注意:我是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
时利用MOXy的外部映射文件。
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>
答案 1 :(得分:0)
我很确定没有标准的方法可以做到这一点。你可以做的是创建两个java类,一个具有层次结构,一个具有扁平结构,并使用java introspection将数据从一个复制到另一个,然后使用第二个创建xml。
答案 2 :(得分: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);
}
}
答案 3 :(得分: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 here。
答案 4 :(得分:-1)
你检查过JiBX(用于将XML数据绑定到Java对象的工具)吗? http://jibx.sourceforge.net/binding/tutorial/binding-structures.html
答案 5 :(得分:-1)
使用@XmlID @XmlIDREF。 并使用所有对象的列表创建类。