2012-12-17 76 views
2

对于follwing例如XML输入:JAXB参考解决

<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参与者指向人从组1读取。 (请参阅下面的代码使用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

以下问题: http://stackoverflow.com/questions/7587095/can-jaxb-marshal-by-遏制 - 第一 - 然后 - 元帅 - xmlidref - 后续 和这篇文章: http://blog.bdoughan.com/2011/09/mixing-nesting-and-references-with.html 可能会提示Xm lAdapter是一种有效的方法... –

+0

[使用JAXB从两个XML文件中交叉引用XmlIDs]的可能的重复(http://stackoverflow.com/questions/5319024/using-jaxb-to-cross-reference-xmlids-from -two-xml-files) –

+0

Blaise感谢你在这里寻找 - 不,它不是那么简单。我一整天都尝试过所有口味的XmlAdapter,但它不起作用。我可能必须扩展这个问题,以便Person是一个基类,并且引用需要指向Person的派生类。我很难让XmlAdapters正确拾取。 –

回答

1

为了更好地说明我已经修改,以适应他的回答这个问题的地步。现在有一个通用的基类人员,我试图按照使用它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多种模式:

  • 它可以一类的注释
  • 它可以是一个字段(吸气剂)的注释
  • 它可以在一个package-info.java文件用于注释的整体封装

在这一点上,我使用所有三个注释,现在必须调试哪些是必要的。我假设全局注释(类,包)没有按预期工作。原因可能是显式调用类型的@XmlElementWrapper中的type = usage。我个人不明白发生了什么。至少事情现在按预期工作。

局部场注解现在是例如: -

@XmlElementWrapper(name="workers") 
@XmlElement(name="Worker", type=Worker.class) 
@XmlJavaTypeAdapter(WorkerAdapter.class) 

的包信息。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; 
    } 

}