【问题标题】:JAXB marshall/unmarshall objects defined by plugins插件定义的 JAXB 编组/解组对象
【发布时间】:2019-09-08 10:08:22
【问题描述】:

让我们假设以下场景:

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

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

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

Fact 是我不知道其实现但由插件提供的接口。可以要求插件提供任意代码,但我想让它尽可能简单。

我想将这些对象保存并加载到 xml。请注意,在加载 xml 时,可能并非所有实现都存在(xml 可能是用一组不同的插件编写的)。我希望能够在再次保存时仍然读取 xml 并且不会丢失任何信息。换句话说:我愿意在类中添加诸如List&lt;Element&gt;List&lt;String&gt;之类的字段,并且在读取xml时,应将存在插件的所有部分读入相应的Facts,而所有部分没有插件的应该存储在ElementString 中,当再次保存时,两个列表都会被保存并且可以被具有所有插件的程序读取。

如何最好地使用 JAXB 实现这一点?

我可以看到的一种方法是使用 Map&lt;Class, org.w3c.dom.Element&gt; 而不是 List&lt;Fact&gt;,它可以通过 JaxB 转换为 xml,然后让任何插件使用 org.w3c.dom API 提供自定义代码与“他们的”元素之间的转换,但是使用那个API有点麻烦,不知道有没有更好的方法?

【问题讨论】:

  • 告诉我们你到目前为止尝试了什么,结果如何,什么是好的,还有什么问题。
  • @metallurg:但我做到了:在最后一段中——我什至说我为什么不喜欢那个解决方案。如果它有助于理解场景,我当然可以提供一些近似代码,但我的麻烦是没有找到“一个”解决方案,我正在寻找一个充其量提供简单客户端界面的 JAXB 惯用解决方案。如果有人能指点一下,我也很高兴。

标签: java jaxb


【解决方案1】:

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

JAXB 不适用于接口;它可以做的最好的就是一个抽象类。这意味着您需要使用List&lt;Object&gt;List&lt;AbstractFact&gt;。 (但您可以在 getter、pluginresolver 或 afterUnmarshall() 中强制执行一些限制)。

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

在 xml 中,您需要有一个像这样的类型标记:&lt;fact xsi:type=\"aFact\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"&gt;。如果您使用 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;
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多