JaxB参考解析

时间:2012-12-17 12:41:00

标签: jaxb unmarshalling

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

1 个答案:

答案 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似乎有多种模式:

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

此时我正在使用所有三个注释,现在必须调试哪些是必要的。我假设全局注释(类,包)没有按预期工作。原因可能是@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;
    }

}
相关问题