2

我有一个要求,根据字段上标记的自定义注释来编组/解组 java pojo 的某些元素。假设我的 java pojp 中有 3 个字段

@CustomVersion("v1")
private String field1;
@CustomVersion("v1","v2")
private String field2;
@CustomVersion("v2")
private String field3;

如果我在 jaxb 中转换时传递 version="v1" 参数,我只想编组带有 v1 的字段。如果我通过 v2,则所有带有 v2 注释的字段都应该只被编组。

甚至可以使用 jaxb 吗?我确信通过某些库或方式会支持选择性编组,但经过相当多的搜索后仍然无法弄清楚。非常感谢任何帮助或建议或指示。

4

2 回答 2

6

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

下面是一个示例,说明如何使用 MOXy 的@XmlNamedObjectGraphs扩展来映射您的用例。

Java 模型

@XmlNamedObjectGraphs扩展允许您指定由键标识的多个映射子集。

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

import org.eclipse.persistence.oxm.annotations.XmlNamedAttributeNode;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraph;
import org.eclipse.persistence.oxm.annotations.XmlNamedObjectGraphs;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlNamedObjectGraphs({
    @XmlNamedObjectGraph(
        name="v1",
        attributeNodes = { 
            @XmlNamedAttributeNode("field1"),
            @XmlNamedAttributeNode("field2")}),
    @XmlNamedObjectGraph(
        name="v2",
        attributeNodes = { 
            @XmlNamedAttributeNode("field2"),
            @XmlNamedAttributeNode("field3")})
})
public class Foo {

    private String field1 = "ONE";
    private String field2 = "TWO";
    private String field3 = "THREE";

}

jaxb.properties

要将 MOXy 用作您的 JAXB 提供程序,您需要包含一个jaxb.properties使用以下条目调用的文件(请参阅: http ://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html )。

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

演示代码

演示

您可以指定与对象图对应的键,以将该子集应用于您正在编组的对象。

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import org.eclipse.persistence.jaxb.MarshallerProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Foo.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        Foo foo = new Foo();

        // Marshal Everything
        marshaller.marshal(foo, System.out);

        // Marshal "v1" Data
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v1");
        marshaller.marshal(foo, System.out);

        // Marshal "v2" Data
        marshaller.setProperty(MarshallerProperties.OBJECT_GRAPH, "v2");
        marshaller.marshal(foo, System.out);
    }

}

输出

<?xml version="1.0" encoding="UTF-8"?>
<foo>
   <field1>ONE</field1>
   <field2>TWO</field2>
   <field3>THREE</field3>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
   <field1>ONE</field1>
   <field2>TWO</field2>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<foo>
   <field2>TWO</field2>
   <field3>THREE</field3>
</foo>

了解更多信息

于 2013-10-22T18:55:33.190 回答
3

首先,我建议在编组之前进行这样的预处理。这会容易得多。但是,如果由于某种原因不可能,那么您可以创建自定义类型适配器。然后,您可以@XmlJavaTypeAdapter(VersioningAdapter.class)添加要启用版本控制的每种类型。 @XmlJavaTypeAdapter也可以在包级别指定,但您必须指定它适用于哪些类型。XmlAdapter如果不指定某处,则无法使用@XmlJavaTypeAdapter

这种解决方案的缺点:

  • 如果您有多个版本化类型,则必须对它们中的每一个进行注释@XmlJavaTypeAdapter
  • @XmlJavaTypeAdapter不适用于根元素,仅适用于子元素。在编组之前,您必须在根元素上手动调用适配器

AFAIK 没有其他选项可以自定义 JAXB 编组。这就是为什么我认为注释处理应该在编组之前在单独的步骤中执行。除非你能接受提到的限制。

示例适配器(完整代码可以在这里找到):

public class VersioningAdapter extends XmlAdapter<Object, Object> {

    @Override
    public Object unmarshal(Object v) throws Exception {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Object marshal(Object v) throws Exception {
        if (v == null) {
            return v;
        }
        Field[] fields = v.getClass().getDeclaredFields();
        for (Field field : fields) {
            Annotation[] annotations = field.getDeclaredAnnotations();
            CustomVersion annotation = findCustomVersion(annotations);
            if (annotation != null) {
                if (!contains(annotation, Configuration.getVersion())) {
                    field.setAccessible(true);
                    field.set(v, null);
                }
            }
        }
        return v;
    }

    private CustomVersion findCustomVersion(Annotation[] annotations) {
        for (Annotation annotation : annotations) {
            if (annotation instanceof CustomVersion) {
                return (CustomVersion) annotation;
            }
        }
        return null;
    }

    private boolean contains(CustomVersion annotation, String version) {
        String[] values = annotation.value();
        for (String value : values) {
            if (value.equals(version)) {
                return true;
            }
        }
        return false;
    }

}
于 2013-10-22T08:33:57.580 回答