JaxB reference resolving

前端 未结 1 1881
暖寄归人
暖寄归人 2021-01-21 11:39

For the follwing example XML input:


  
    
    
  &l         


        
相关标签:
1条回答
  • 2021-01-21 12:01

    To better illustrate the point I have modified the question to fit his answer. There is now a generic base class Person and I am trying to use it as per Can generic XmlAdapter be written

    I solved the issue of being able to actually make sure the Adapters are used by writing specific derived Classes and using them with @XmlJavaTypeAdapter. I preregister the adapters using:

    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());
    

    and then unmarshalling twice. The debug shows that the Adapter instance for both passes is the same. Still the lookup somehow seemed to fail ... The reason was the way the @XmlJavaTypeAdapter annotation works see:

    What package-info do I annotate with XmlJavaTypeAdapters?

    There seem to be multiple modes for @XmlJavaTypeAdapter:

    • it can be an annotation for a class
    • it can be an annotation for a field (getter)
    • it can be used in a package-info.java file to annotate a whole package

    At this point I am using all three annotations and now have to debug which ones are necessary. I assume the global annotations (class,package) are not working as expected. The reason might be the type= usage in the @XmlElementWrapper which explicitly calls for a type. Personally I do not understand what is going on yet. At least things are now working as expected.

    the local field annotation is now e.g.:

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

    the package-info.java annotation is:

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

    the class annotation is:

    @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;
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题