我如何编组对象层次结构,以便组件对象不成为嵌套的 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 来解决这个问题的大部分问题。事实上,这些对我的具体情况提出了一些限制:
Address不会包含任何其他复杂类型。我正在查看@XmlPathMOXy 的注释,但我不知道如何获取(C)中的节点名称前缀。
我梦想有一个解决方案,其中一些提供正确 JAXB 注释的 xjc 自定义可以让我继续前进,但从我目前的搜索来看,这似乎不太可能。任何非 JAXB 解决方案就足够了。
我使用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)