Search code examples
javajsongsonxmlgregoriancalendar

How to de-serialize XMLGregorianCalender with Gson?


When processing a Json string with XMLGregorianCalender using Gson I get the exception:

java.lang.RuntimeException: Failed to invoke public javax.xml.datatype.XMLGregorianCalendar() with no args

The object which is de-serialized by fromJson(..) with Gson has a XMLGregorianCalender object.

What can be the possible solution for the above error?


Solution

  • Abstract class javax.xml.datatype.XMLGregorianCalendar can not be a instantiated by its default/no-args constructor which makes GSON to fail.

    If you resolve the class that extends above mentioned and that class has a public no-args constructor you can de-serialize that directly. For example, in my case:

    XMLGregorianCalendar xmlGC = gson.fromJson(strXMLGC,
            com.sun.org.apache.xerces.internal
                .jaxp.datatype.XMLGregorianCalendarImpl.class);
    

    A generic way to get it working everywhere - without caring the implementing class - is to define a custom JsonDeserializer. To make de-serialing easy you could first create an adapter class that holds the data that XMLGregorianCalendar's JSON has:

    @Getter
    public class XMLGregoriancalendarAdapterClass {
        private BigInteger year;
        private int month, day, timezone,  hour, minute, second;
        private BigDecimal fractionalSecond;        
    }
    

    Data types of each field in above class are chosen to match one specific method for constructing XMLGregorianCalendar with javax.xml.datatype.DatatypeFactory.

    Having above adapter class create a de-serialiazer like:

    public class XMLGregorianCalendarDeserializer
            implements JsonDeserializer<XMLGregorianCalendar> {
        @Override
        public XMLGregorianCalendar deserialize(JsonElement json, Type typeOfT,
                JsonDeserializationContext context) throws JsonParseException {
            // Easily parse the adapter class first
            XMLGregoriancalendarAdapterClass ac = 
                    new Gson().fromJson(json, 
                            XMLGregoriancalendarAdapterClass.class);
            try {
                // Then return a new newXMLGregorianCalendar
                // using values in adapter class
                return DatatypeFactory.newInstance()
                        .newXMLGregorianCalendar(ac.getYear(), ac.getMonth(), 
                                ac.getDay(), ac.getHour(),        
                                ac.getMinute(), ac.getSecond(),
                                ac.getFractionalSecond(), ac.getTimezone());
            } catch (DatatypeConfigurationException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    

    Using above you can construct GSON like:

    Gson gson = new GsonBuilder().setPrettyPrinting()
                    .registerTypeAdapter(
                         XMLGregorianCalendar.class,
                         new XMLGregorianCalendarDeserializer() )
                    .create();
    

    after which it is just:

    XMLGregorianCalendar xmlGC2 = 
            gson.fromJson(json, YourClassHavingXMLGregorianCalendar.class);