对于以下示例XML输入:
<Participants course="someCourse">
<workers>
<Worker ref="p3">
<Worker ref="p2">
</workers>
<Trainer ref="p1"/>
</Participants>
<Group id="group1" name="some mixed Person group">
<trainers>
<Trainer id="p1" name="John Doe">
</trainers>
<workers>
<Worker id="p2" name="Jim Scott">
<Worker id="p3" name="Walter Peace">
</workers>
</Group>
我正在尝试确保参与者中的PersonList指向从group1读取的人员。 (有关使用的JaxB注释,请参阅下面的代码片段)。这只是更通用的一个例子 我正在寻求的方法。我需要通常能够以某种方式遵循id =“”和ref =“”属性 列表元素被正确地解组为引用。
使用UnmarshalListener和Unmarshalling两次,我解决了从ref属性到id属性的引用问题。在第一阶段,从id属性填充查找映射。在第二阶段,查询参考。不幸的是,此解决方案将创建副本而不是引用。我可以使用父对象来解决这个问题,但我正在寻找更通用的解决方案。以所示方式使用ref / id属性实现正确解除引用的好方法是什么?
/**
* intercept the unmarshalling
*/
public static class ModelElementMarshallerListener extends javax.xml.bind.Unmarshaller.Listener {
public Map<String,Person> lookup=new HashMap<String,Person>();
@Override
public void afterUnmarshal(java.lang.Object target, java.lang.Object parent) {
if (target instanceof Person) {
person=(Person) target;
if (person.getId()!=null) {
lookup.put(person.getId(), person);
}
if (person.getRef()!=null) {
if (lookup.containsKey(person.getRef())) {
Person personRef=lookup.get(person.getRef());
person.copyFrom(personRef);
person.setRef(null);
}
}
}
}
}
@XmlRootElement(name="Participants")
public class Participants {
private List<Worker> workers;
/**
* getter for List<Worker> workers
* @return workers
*/
@XmlElementWrapper(name="workers")
@XmlElement(name="Worker", type=Worker.class)
public List<Worker> getWorkers() {
return workers;
}
...
}
@XmlRootElement(name="Group")
public class Group {
private List<Worker> workers;
/**
* getter for List<Worker> workers
* @return workers
*/
@XmlElementWrapper(name="workers")
@XmlElement(name="Worker", type=Worker.class)
public List<Worker> getWorkers() {
return workers;
}
...
}
@XmlRootElement(name="Trainer")
public class Trainer extends Person {}
@XmlRootElement(name="Worker")
public class Worker extends Person {}
@XmlRootElement(name="Person")
public class Person {
private String name;
/**
* getter for xsd:string/String name
* @return name
*/
@XmlAttribute(name="name")
public String getName() {
return name;
}
public void setName(String name) {
this.name=name;
}
private String ref;
/**
* getter for xsd:string/String id
* @return id
*/
@XmlAttribute(name="ref")
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref=ref;
}
private String id;
/**
* getter for xsd:string/String id
* @return id
*/
@XmlAttribute(name="id")
@XmlID
public String getId() {
this.id;
}
/**
* setter for xsd:string/String id
* @param pid - new value for id
*/
public void setId(String pid) {
this.id=pid;
}
}
答案 0 :(得分:1)
为了更好地说明我已修改问题以适合他的答案。现在有一个通用基类Person,我试图按照Can generic XmlAdapter be written
使用它我解决了通过编写特定的派生类并将它们与@XmlJavaTypeAdapter一起使用来实际确保使用适配器的问题。我使用以下方法预先注册适配器:
JAXBContext context = JAXBContext.newInstance(type);
Unmarshaller u = context.createUnmarshaller();
u.setAdapter(Worker.WorkerAdapter.class,new Worker.WorkerAdapter());
u.setAdapter(Trainer.TrainerAdapter.class,new Trainer.TrainerAdapter());
然后解组两次。调试显示两个传递的Adapter实例是相同的。仍然以某种方式查找失败......原因是@XmlJavaTypeAdapter注释的工作方式见:
What package-info do I annotate with XmlJavaTypeAdapters?
@XmlJavaTypeAdapter似乎有多种模式:
此时我正在使用所有三个注释,现在必须调试哪些是必要的。我假设全局注释(类,包)没有按预期工作。原因可能是@XmlElementWrapper中的type = usage显式调用了一个类型。就个人而言,我还不明白发生了什么。至少事情现在按预期工作。
本地字段注释现在是例如:
@XmlElementWrapper(name="workers")
@XmlElement(name="Worker", type=Worker.class)
@XmlJavaTypeAdapter(WorkerAdapter.class)
package-info.java注释是:
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value=WorkerAdapter.class,type=Worker.class),
@XmlJavaTypeAdapter(value=TrainerAdapter.class,type=Trainer.class),
})
package com.bitplan.jaxb.refidtest;
import javax.xml.bind.annotation.adapters。*;
类注释是:
@XmlJavaTypeAdapter(Worker.WorkerAdapter.class)
public class Worker extends Person {
...
/**
* Worker Adapter
* @author wf
*
*/
public static class WorkerAdapter extends PersonAdapter<Worker>{
@Override
public Worker marshal(Worker me)
throws Exception {
return super.marshal(me);
}
@Override
public Worker unmarshal(Worker me) throws Exception {
return super.unmarshal(me);
}
}
/**
* https://stackoverflow.com/questions/7587095/can-jaxb-marshal-by-containment-at-first-then-marshal-by-xmlidref-for-subsequen/7587727#7587727
* @author wf
*
*/
public class PersonAdapter<T extends Person> extends XmlAdapter<T, T>{
public boolean debug=true;
/**
* keep track of the elements already seen
*/
public Map<String,T> lookup=new HashMap<String,T>();
@Override
public T marshal(T me)
throws Exception {
return me;
}
/**
* show debug information
* @param title
* @param key
* @param me
* @param found
*/
public void showDebug(String title,String key,T me, T found) {
String deref="?";
if (found!=null)
deref="->"+found.getId()+"("+found.getClass().getSimpleName()+")";
if (debug)
System.err.println(title+": "+key+"("+me.getClass().getSimpleName()+")"+deref+" - "+this);
}
@Override
public T unmarshal(T me) throws Exception {
if (me.getId()!=null) {
showDebug("id",me.getId(),me,null);
lookup.put(me.getId(), me);
return me;
}
if (me.getRef()!=null) {
if (lookup.containsKey(me.getRef())) {
T meRef=lookup.get(me.getRef());
showDebug("ref",me.getRef(),me,meRef);
me.setRef(null);
return meRef;
} else {
if (debug)
showDebug("ref",me.getRef(),me,null);
}
}
return me;
}
}