1

I'm developing some JavaSE GUI application which has to store and load it's data from/to XML file (source code avaliable here: https://github.com/SP8EBC/MKS_JG)

In some place of the data structure I've a HashMap which bond a base type to Short. In practice this base type is an abstract class, so my software adds object of child classes as a keys. To marshall and unmarshall these types I developed an adapter, but unfortunatelly whatever I did with annotations this adapter is never used, so the output XML file is wrong. I put some breakpoints inside methods and on Debug software newer stops on these breaks.

My questions is: What should I do inside the code to correctly handle this HashMap with my Adapter?

The class where the HashMap with base class exists is like that:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Competition {
 // some other fields and methods 
    @XmlElement
    public HashMap<LugerCompetitor, Short> startList;
}

At this point declaration of the base class looks like this

@XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
@XmlJavaTypeAdapter(value = LugerCompetitorAdapter.class)
public abstract class LugerCompetitor {
    /**
     * .... some commentary in polish 
     */ 
    public abstract CompetitionTypes getCompetitorType();

    public abstract void setStartNumber(short num);
    @XmlTransient
    public abstract short getStartNumber();

    public abstract String toString();
}

And one of the child classes looks like this

public class LugerDouble extends LugerCompetitor {

    /**
     * Saneczkarz na górze
     */
    public Luger upper;

    /**
     * Saneczkarz na dole
     */
    public Luger lower;

    short startNum;

    @Override
    public CompetitionTypes getCompetitorType() {
        // TODO Auto-generated method stub
        return null;
    }

    public String toString() {
        String out;

        out = upper.surname + " / " + lower.surname;

        return out;
    }

    @Override
    public short getStartNumber() {
        return this.startNum;
    }

    @Override
    public void setStartNumber(short num) {
        this.startNum = num;

    }

}

As You see I wrote an Adapter which should convert totally different child objects to something standarized in the XML file. The adapter looks as follows:

public class LugerCompetitorAdapter extends XmlAdapter<LugerCompetitorAdapter.AdaptedCompetitorLuger, LugerCompetitor> {

    public static class AdaptedCompetitorLuger {

        @XmlAttribute
        public long lugerSystemId;          // LugerSingle

        @XmlAttribute
        public long bottomLugerSystemId;    // Dwójka sankowa - dół

        @XmlAttribute
        public long upperLugerSystemId;     // Dwójka sankowa - góra

        @XmlAttribute
        public long maleLugerSystemId;      // Sztafeta albo drużyna

        @XmlAttribute
        public long femaleSystemId;         // jw

        @XmlAttribute       
        public long doubleSystemId;         // jw


    }

    @Override
    public AdaptedCompetitorLuger marshal(LugerCompetitor v) throws Exception {

        AdaptedCompetitorLuger adaptedCompetitorLuger = new AdaptedCompetitorLuger();

        if (v instanceof LugerSingle) {
            adaptedCompetitorLuger.lugerSystemId = ((LugerSingle)v).single.getSystemId();
            return adaptedCompetitorLuger;
        }
        if (v instanceof LugerDouble) {
            adaptedCompetitorLuger.bottomLugerSystemId = ((LugerDouble)v).lower.getSystemId();
            adaptedCompetitorLuger.upperLugerSystemId = ((LugerDouble)v).upper.getSystemId();
            return adaptedCompetitorLuger;


        }

        return null;
    }

    @Override
    public LugerCompetitor unmarshal(AdaptedCompetitorLuger v) throws Exception {
        return null;
    }

}

EDIT (Solution added)

So I made what I want in quite another way. I just made an adapter for whole HashMap, instead of trying do marschall and unmarschall LugerCompetitor itself. My solution is below and it seems to work when I'm generating the XML. Opposite direction still need to be developed.

@Component
public class StartListAdapter extends XmlAdapter<StartListAdapter.AdaptedStartList, Map<LugerCompetitor, Short>> {

    RTE_ST rte_st;

    @Autowired
    @Lazy
    public void setRTE(RTE_ST rte) {
        rte_st = rte;
    }

    public static class AdaptedStartList {
        @XmlElement(name="startListEntry")
        public List<AdaptedEntry> adaptedList = new ArrayList<AdaptedEntry>();
    }

    public static class AdaptedEntry {

        @XmlElement(required = false, nillable = true )
        public Long lugerSystemId;          // konkurencja pojedyncza K albo M

        @XmlElement(required = false, nillable = true )
        public Long lowerLugerSystemId; // dwójki sankowe - sankarz na dole

        @XmlElement(required = false, nillable = true )
        public Long upperLugerSystemId;     // j/w ale sankarz na górze

        @XmlElement(required = false, nillable = true )
        public Long maleLugerSystemId;      // M podczas sztafety albo konkurencji drużynowej

        @XmlElement(required = false, nillable = true )
        public Long femaleLugerSystemId;    // K j/w

        @XmlElement(required = true)
        public short startNumber;
    }

    @Override
    public AdaptedStartList marshal(Map<LugerCompetitor, Short> arg0) throws Exception {
        AdaptedStartList out = new AdaptedStartList();

        for (Entry<LugerCompetitor, Short> e : arg0.entrySet()) {
            AdaptedEntry adaptedEntry = new AdaptedEntry();

            LugerCompetitor k = e.getKey();

            if (k instanceof LugerSingle) {
                adaptedEntry.lugerSystemId = ((LugerSingle)k).single.getSystemId();
                adaptedEntry.startNumber = e.getValue();
            }
            else if (k instanceof LugerDouble) {
                adaptedEntry.lowerLugerSystemId = ((LugerDouble)k).lower.getSystemId();
                adaptedEntry.upperLugerSystemId = ((LugerDouble)k).upper.getSystemId();
                adaptedEntry.startNumber = e.getValue();

            }

            out.adaptedList.add(adaptedEntry);
        }

        return out;
    }

    @Override
    public Map<LugerCompetitor, Short> unmarshal(AdaptedStartList arg0) throws Exception {
        return null;
    }

}
Mateusz L
  • 41
  • 1
  • 7

1 Answers1

0

First, if possible but not necessary, convert LugerCompetitor to an interface. You can avoid all public abstract clutter:

public interface LugerCompetitor {
    /**
     * .... some commentary in polish 
     */ 
    CompetitionTypes getCompetitorType();

    void setStartNumber(short num);
    @XmlTransient
    short getStartNumber();
}

Then you can define an element to represent LugerCompetitor and Short value:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class LugerCompetitorValue {
    @XmlElement
    @XmlJavaTypeAdapter(LugerCompetitorAdapter.class)
    public LugerCompetitor competitor;

    @XmlElement
    public Short value;

}

And add a collection of LugerCompetitorValue to your class:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Competition {
 // some other fields and methods 
    @XmlElement
    public List<LugetCompetitorValue> lugerCompetitorValues;
}

Then adapter should start working. The question, however, is how are you expecting to unmarshal the data on the client side?

Mateusz L
  • 41
  • 1
  • 7
Pavel Molchanov
  • 2,299
  • 1
  • 19
  • 24
  • Hi. Generally I converted Interface to an abstract class because JAXB was complaining a lot about it, and after some investigation I realized that JAXB just cannot operate on interface. Generally I'm not able to change the Map to the List of pairs values because I've written a lot of bussiness logic of this SW and this change will require too much changes. – Mateusz L Jun 14 '18 at 16:06
  • And answer to second part od your questions: I'll add to Competitions class a list where all competitors (all copies of LugerCompetitor childs) will be stored with all of the data. This list will be placed at a begining of XML file, to provide that it will be unmraschalled firstly. Then hash-maps etc. will be unmarschalled. Propably I'll inject this initial map by Spring DI @Autowired feature.. – Mateusz L Jun 14 '18 at 16:20
  • Have you checked the answer on this question https://stackoverflow.com/questions/3941479/jaxb-how-to-marshall-map-into-keyvalue-key? Why do you want to handle Short value outside of the LugerCompetitor? – Pavel Molchanov Jun 14 '18 at 18:20
  • I solve my initial problem in another way. Please check edited post. I've some problems with @Autowiring but I think this is because I haven't got deep knowledge about Spring DI... The problem lies in rte_st field which is autowired when I'm initializing the Spring context in main function, not why JAXB is using this adapter. So if rte_st field is not static I haven't got access to them while XML file is processed (field is null when JAXB is calling the adapter) – Mateusz L Jun 14 '18 at 19:19