插件定义的JAXB marshall / unmarshall对象

时间:2019-09-08 10:08:22

标签: java jaxb

让我们假设以下情况:

我有以下类型的对象的列表:

public class MyObject {
   private String name
   private SomeClass someField
   private List<Fact> facts
}

namesomeField字段只是为了表明该类具有一些常规成员。您可以假设知道如何将这些类转换为xml。

Fact是一个接口,其中我不知道实现,但由插件提供。可能需要插件来提供任意代码,但我想使其尽可能简单。

我想保存这些对象并将其加载到xml。请注意,在加载xml时,并非所有实现都可能存在(xml可能是使用一组不同的插件编写的)。我希望能够再次读取xml而不丢失任何信息。换句话说:我愿意在类中添加List<Element>List<String>之类的字段,并且在读取xml时,应该将存在插件的所有部分都读取到相应的{{1}中} s,尽管所有没有插件的部件都应存储在FactElement中,并且再次保存时,两个列表都会被保存,并且可以由具有所有插件的程序读取。

如何最好地使用JAXB做到这一点?

我可以看到的一种方法是使用String而不是Map<Class, org.w3c.dom.Element>,后者可以由JaxB转换为xml,然后让任何插件使用{ {1}} API,但是使用该API有点麻烦,所以我想知道是否有更好的方法?

1 个答案:

答案 0 :(得分:1)

不知道best,但是一种与您描述的相近的方法是:

JAXB无法与接口一起使用;最好能做的就是抽象类。意味着您需要使用List<Object>List<AbstractFact>。 (但是您可以在getter,pluginresolver或afterUnmarshall()中实施一些限制。)

您的插件提供了扩展的基本类(SPI是常用的方法)。您收集它们并(在验证之后)使用它们创建您的JAXBContext。 (如果要支持多个接口,则可以通过不同的方法提供它们。)

在xml中,您需要具有以下类型标记:<fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">。如果使用jaxb创建xml,则会自动创建。 (这些类需要具有@XmlRootElement批注)。

这是一个简化的示例:

interface Fact {

}
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
class R {

    @XmlElement(name = "fact")
    private List<Object> facts;


    @SuppressWarnings("unchecked")
    public List<Fact> getTest() {
        if (facts == null) {
            facts = new ArrayList<>();
        }
        return (List<Fact>) (Object) facts;
    }

    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        // check if all facts implement same interface
        for(Object object:facts) {
            if (!(object instanceof Fact)) {
                throw new IllegalArgumentException("Unsupported type in facts list");
            }
        }
    }
}

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "aFact")
class AFact implements Fact {

    @XmlElement
    private String a;

    public AFact() {
    }

    public AFact(String a) {
        this.a = a;
    }

    public String getA() {
        return a;
    }

    @Override
    public String toString() {
        return "AFact [a=" + a + "]";
    }

}

public class Jax {
    public static void main(String[] args) throws JAXBException {

        String xml = "<r><fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"><a>ba</a></fact></r>";

        List<Class<?>> contextClasses = new ArrayList<>();
        contextClasses.add(R.class);
        contextClasses.addAll(getClassesFromPlugin());
        JAXBContext context = JAXBContext.newInstance(contextClasses.toArray(new Class<?>[0]));
        R entity = (R) context.createUnmarshaller().unmarshal(new StringReader(xml));

        System.out.println(entity.getTest());

        R r = new R();
        r.getTest().add(new AFact("ab"));

        context.createMarshaller().marshal(r, System.out);
    }

    private static List<Class<?>> getClassesFromPlugin() {
        List<Class<?>> asList = Arrays.asList(AFact.class);
        for(Class<?> cls:asList) {
            if (!Fact.class.isAssignableFrom(cls)) {
                throw new IllegalArgumentException("Unsupported class");
            }
        }
        return asList;
    }
}
相关问题