3
votes

Is there a way using JAXB to unmarshal/split a single attribute into multiple fields?

I have an element in a XML file that has a time attribute (XML file cannot be changed). The precision of the time attribute is to microseconds. Therefore I cannot store the attribute in a java Date or Joda DateTime field.

I want to store the attribute in a simple custom DateTime class where the subseconds are stored in an Integer and the rest in a java Date (I don't have access to Joda). However, I've been unable to figure out how to unmarshal the single attribute to both the Date and Integer.

XML example:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root>
        <Range start="2001-01-01 01:23:45.012345"/>
    </root>

Simple custom DateTime class:

public class DateTime {
    Date date;
    Integer subseconds;

    public Date getDate() {
        return this.date;
    }
    public void setDate(Date date) {
        this.date = date;
    }

    public Integer getSubseconds() {
        return this.subseconds;
    }
    public void setSubseconds(Integer subseconds) {
        this.subseconds = subseconds;
    }
}

Final Solution

Created DateTimeAdapter class as suggested by @Blaise Doughan:

public class DateTimeAdapter extends XmlAdapter<String, DateTime> {
    @Override
    public String marshal (DateTime v) {
        return v.toString();
    }

    @Override
    public DateTime unmarshal (String v) {
        return new DateTime(v);
    }
}

Updated custom DateTime class (added constructor and made it immutable):

public class DateTime {
    Date date = new Date();
    Integer subseconds = 0;

    public DateTime(String dateString) {
        if (dateString.matches("^\\d{4}-{\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}\\.\\d+") {
            try {
                this.date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateString.substring(0, 19));
            }
            catch (ParseException ex) {
                ex.printStackTrace();
            }

            this.subseconds = Integer.valueOf(dateString.substring(20));
        }
    }

    public Date getDate() {
        return this.date;
    }

    public Integer getSubseconds() {
        return this.subseconds;
    }

    ...additional methods...
}

I chose to do the string translation in the custom DateTime class constructor instead of the XmlAdapter so that I could use DateTime in other areas besides just unmarshalling.

Reference to adapter was as below:

    @XmlAttribute(name = "start")
    @XmlJavaTypeAdapter(DateTimeAdapter.class)
    private DateTime range;
2

2 Answers

5
votes

You could leverage an XmlAdapter. Your XmlAdapter would be responsible for converting your instance of DateTime to/from a String.

public class DateTimeAdapter extends XmlAdapter<String, DateTime> {
    ...
}

Then you would reference the XmlAdapter using the @XmlJavaTypeAdapter annotation on the property of type DateTime.

@XmlJavaTypeAdapter(DateTimeAdapter.class)
public DateTime getFoo() {
    return foo;
}
0
votes
String[] dt=start.split(".");
String timeStr=dt[0];
String subsStr=dt.length>1?dt[1]:null;