2

对于以下示例 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>

我试图确保 Participants 中的 PersonList 指向从 group1 读取的 Persons。(有关使用的 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; 
  }
}
4

1 回答 1

1

为了更好地说明这一点,我修改了问题以适合他的答案。现在有一个通用基类 Person ,我正在尝试按照Can generic XmlAdapter bewritten使用它

我解决了能够通过编写特定的派生类并将它们与@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());

然后解组两次。调试显示两次传递的适配器实例是相同的。查找仍然以某种方式似乎失败了......原因是@XmlJavaTypeAdapter 注释的工作方式,请参阅:

我应该用 XmlJavaTypeAdapters 注释哪些包信息?

@XmlJavaTypeAdapter 似乎有多种模式:

  • 它可以是一个类的注解
  • 它可以是字段的注释(getter)
  • 它可以在 package-info.java 文件中用于注释整个包

此时我正在使用所有三个注释,现在必须调试哪些是必要的。我假设全局注释(类、包)没有按预期工作。原因可能是 @XmlElementWrapper 中的 type= 用法,它显式地调用了一个类型。就我个人而言,我还不明白发生了什么。至少现在一切都按预期进行。

本地字段注释现在是例如:

@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;

导入 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;
    }

}
于 2012-12-17T18:42:22.437 回答