Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/MOXy/Mapping the Unmappable/XML Transformations"

m
m
Line 5: Line 5:
 
}}
 
}}
 
=Transformation Mapping=
 
=Transformation Mapping=
 +
In many cases, you can use MOXy's transformation mapping ('''@XmlTransformation''') to map an unmapple object in JAXB.
 +
 +
For example, if you wanted to map the following XML to objects and combine the values of '''DATE''' and '''TIME''' into a single '''java.util.Date''' object, you can use a transformation mapping.
 +
 +
<tt>
 +
<ELEM_B>
 +
    <B_DATE>20100825</B_DATE>
 +
    <B_TIME>153000</B_TIME>
 +
    <NUM>123</NUM>
 +
    <C_DATE>20100825</C_DATE>
 +
    <C_TIME>154500</C_TIME>
 +
</ELEM_B>
 +
</tt>
 +
 +
'''Note:''' Ordinarily, you would use '''@XmlAdapter'''. However:
 +
* Although the '''DATE/TIME''' pairings are repeated throughout the document, the element name changes ''each time'' (such as '''B_DATE/B_TIME''', '''C_DATE/C_TIME''', and so on).
 +
* Because each pairing is missing a grouping element, you would need to adapt ''the entire'' '''ElemB''' class.
 +
 +
 +
 +
 +
In this case the transformation mapping concept in MOXy will make this use case much easier to map.  Let's look at how this looks:
 +
 +
<source lang="Java">
 +
 +
import java.util.Date;
 +
 +
import javax.xml.bind.annotation.XmlAccessType;
 +
import javax.xml.bind.annotation.XmlAccessorType;
 +
import javax.xml.bind.annotation.XmlElement;
 +
import javax.xml.bind.annotation.XmlRootElement;
 +
 +
import org.eclipse.persistence.oxm.annotations.XmlReadTransformer;
 +
import org.eclipse.persistence.oxm.annotations.XmlTransformation;
 +
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformer;
 +
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformers;
 +
 +
@XmlAccessorType(XmlAccessType.FIELD)
 +
@XmlRootElement(name="ELEM_B")
 +
public class ElemB {
 +
 +
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
 +
    @XmlWriteTransformers({
 +
        @XmlWriteTransformer(xmlPath="B_DATE/text()", transformerClass=DateFieldTransformer.class),
 +
        @XmlWriteTransformer(xmlPath="B_TIME/text()", transformerClass=TimeFieldTransformer.class),
 +
    })
 +
    private Date bDate;
 +
 +
    @XmlElement(name="NUM")
 +
    private int num;
 +
 +
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
 +
    @XmlWriteTransformers({
 +
        @XmlWriteTransformer(xmlPath="C_DATE/text()", transformerClass=DateFieldTransformer.class),
 +
        @XmlWriteTransformer(xmlPath="C_TIME/text()", transformerClass=TimeFieldTransformer.class),
 +
    })
 +
    private Date cDate;
 +
 +
}
 +
 +
</source>
 +
 +
XML Read Transformer
 +
 +
An XML read transformer is responsible for constructing the object value from XML.
 +
 +
1
 +
2
 +
3
 +
4
 +
5
 +
6
 +
7
 +
8
 +
9
 +
10
 +
11
 +
12
 +
13
 +
14
 +
15
 +
16
 +
17
 +
18
 +
19
 +
20
 +
21
 +
22
 +
23
 +
24
 +
25
 +
26
 +
27
 +
28
 +
29
 +
30
 +
31
 +
32
 +
33
 +
34
 +
35
 +
36
 +
37
 +
 +
import java.text.ParseException;
 +
import java.text.SimpleDateFormat;
 +
 +
import org.eclipse.persistence.internal.helper.DatabaseField;
 +
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
 +
import org.eclipse.persistence.mappings.transformers.AttributeTransformer;
 +
import org.eclipse.persistence.sessions.Record;
 +
import org.eclipse.persistence.sessions.Session;
 +
 +
public class DateAttributeTransformer implements AttributeTransformer {
 +
 +
    private AbstractTransformationMapping mapping;
 +
    private SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss");
 +
 +
    public void initialize(AbstractTransformationMapping mapping) {
 +
        this.mapping = mapping;
 +
    }
 +
 +
    public Object buildAttributeValue(Record record, Object instance, Session session) {
 +
        try {
 +
            String dateString = null;
 +
            String timeString = null;
 +
           
 +
            for(DatabaseField field : mapping.getFields()) {
 +
                if(field.getName().contains("DATE")) {
 +
                    dateString = (String) record.get(field);
 +
                } else {
 +
                    timeString = (String) record.get(field);
 +
                }
 +
            }
 +
            return yyyyMMddHHmmss.parseObject(dateString + timeString);
 +
        } catch(ParseException e) {
 +
            throw new RuntimeException(e);
 +
        }
 +
    }
 +
 +
}
 +
 +
XML Write Transformer(s)
 +
 +
An XML write transformer is responsible for constructing an XML value from the object.  A transformation mapping may have multiple write transformers.  For this example we will have two.
 +
 +
The first XML write transformer is responsible for writing out the year, month, and day information in the format yyMMdd.
 +
 +
1
 +
2
 +
3
 +
4
 +
5
 +
6
 +
7
 +
8
 +
9
 +
10
 +
11
 +
12
 +
13
 +
14
 +
15
 +
16
 +
17
 +
18
 +
19
 +
20
 +
21
 +
22
 +
 +
import java.text.SimpleDateFormat;
 +
import java.util.Date;
 +
 +
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
 +
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
 +
import org.eclipse.persistence.sessions.Session;
 +
 +
public class DateFieldTransformer implements FieldTransformer {
 +
 +
    private AbstractTransformationMapping mapping;
 +
    private SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
 +
 +
    public void initialize(AbstractTransformationMapping mapping) {
 +
        this.mapping = mapping;
 +
    }
 +
 +
    public Object buildFieldValue(Object instance, String xPath, Session session) {
 +
        Date date = (Date) mapping.getAttributeValueFromObject(instance);
 +
        return yyyyMMdd.format(date);
 +
    }
 +
 +
}
 +
 +
The second XML write transformer is responsible for writing out the hour, minute, and second information in the format HHmmss.
 +
 +
 +
1
 +
2
 +
3
 +
4
 +
5
 +
6
 +
7
 +
8
 +
9
 +
10
 +
11
 +
12
 +
13
 +
14
 +
15
 +
16
 +
17
 +
18
 +
19
 +
20
 +
21
 +
22
 +
 +
import java.text.SimpleDateFormat;
 +
import java.util.Date;
 +
 +
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
 +
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
 +
import org.eclipse.persistence.sessions.Session;
 +
 +
public class TimeFieldTransformer implements FieldTransformer {
 +
 +
    private AbstractTransformationMapping mapping;
 +
    private SimpleDateFormat HHmmss = new SimpleDateFormat("HHmmss");
 +
 +
    public void initialize(AbstractTransformationMapping mapping) {
 +
        this.mapping = mapping;
 +
    }
 +
 +
    public Object buildFieldValue(Object instance, String xPath, Session session) {
 +
        Date date = (Date) mapping.getAttributeValueFromObject(instance);
 +
        return HHmmss.format(date);
 +
    }
 +
 +
}
 +
 +
jaxb.properties
 +
 +
In order to use the MOXy JAXB implementation you need to add a jaxb.properties file in which your model classes with the following entry:
 +
 +
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
 +
 +
Demo
 +
 +
The following class can be used to demonstrate the mapping:
 +
1
 +
2
 +
3
 +
4
 +
5
 +
6
 +
7
 +
8
 +
9
 +
10
 +
11
 +
12
 +
13
 +
14
 +
15
 +
16
 +
17
 +
18
 +
19
 +
20
 +
21
 +
 +
import java.io.File;
 +
 +
import javax.xml.bind.JAXBContext;
 +
import javax.xml.bind.Marshaller;
 +
import javax.xml.bind.Unmarshaller;
 +
 +
public class Demo {
 +
 +
    public static void main(String[] args) throws Exception {
 +
        JAXBContext jc = JAXBContext.newInstance(ElemB.class);
 +
 +
        Unmarshaller unmarshaller = jc.createUnmarshaller();
 +
        File xml = new File("input.xml");
 +
        ElemB elemB = (ElemB) unmarshaller.unmarshal(xml);
 +
 +
        Marshaller marshaller = jc.createMarshaller();
 +
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
 +
        marshaller.marshal(elemB, System.out);
 +
    }
 +
 +
}
 +
Please Note
  
  

Revision as of 09:49, 6 April 2011

EclipseLink MOXy

Eclipselink-logo.gif
EclipseLink
Website
Download
Community
Mailing ListForumsIRCmattermost
Issues
OpenHelp WantedBug Day
Contribute
Browse Source

Transformation Mapping

In many cases, you can use MOXy's transformation mapping (@XmlTransformation) to map an unmapple object in JAXB.

For example, if you wanted to map the following XML to objects and combine the values of DATE and TIME into a single java.util.Date object, you can use a transformation mapping.

<ELEM_B>

   <B_DATE>20100825</B_DATE>
   <B_TIME>153000</B_TIME>
   <NUM>123</NUM>
   <C_DATE>20100825</C_DATE>
   <C_TIME>154500</C_TIME>

</ELEM_B>

Note: Ordinarily, you would use @XmlAdapter. However:

  • Although the DATE/TIME pairings are repeated throughout the document, the element name changes each time (such as B_DATE/B_TIME, C_DATE/C_TIME, and so on).
  • Because each pairing is missing a grouping element, you would need to adapt the entire ElemB class.



In this case the transformation mapping concept in MOXy will make this use case much easier to map. Let's look at how this looks:

 
import java.util.Date;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
 
import org.eclipse.persistence.oxm.annotations.XmlReadTransformer;
import org.eclipse.persistence.oxm.annotations.XmlTransformation;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformer;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformers;
 
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="ELEM_B")
public class ElemB {
 
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
    @XmlWriteTransformers({
        @XmlWriteTransformer(xmlPath="B_DATE/text()", transformerClass=DateFieldTransformer.class),
        @XmlWriteTransformer(xmlPath="B_TIME/text()", transformerClass=TimeFieldTransformer.class),
    })
    private Date bDate;
 
    @XmlElement(name="NUM")
    private int num;
 
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
    @XmlWriteTransformers({
        @XmlWriteTransformer(xmlPath="C_DATE/text()", transformerClass=DateFieldTransformer.class),
        @XmlWriteTransformer(xmlPath="C_TIME/text()", transformerClass=TimeFieldTransformer.class),
    })
    private Date cDate;
 
}

XML Read Transformer

An XML read transformer is responsible for constructing the object value from XML.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

import java.text.ParseException; import java.text.SimpleDateFormat;

import org.eclipse.persistence.internal.helper.DatabaseField; import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping; import org.eclipse.persistence.mappings.transformers.AttributeTransformer; import org.eclipse.persistence.sessions.Record; import org.eclipse.persistence.sessions.Session;

public class DateAttributeTransformer implements AttributeTransformer {

   private AbstractTransformationMapping mapping;
   private SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss");

   public void initialize(AbstractTransformationMapping mapping) {
       this.mapping = mapping;
   }

   public Object buildAttributeValue(Record record, Object instance, Session session) {
       try {
           String dateString = null;
           String timeString = null;
            
           for(DatabaseField field : mapping.getFields()) {
               if(field.getName().contains("DATE")) {
                   dateString = (String) record.get(field);
               } else {
                   timeString = (String) record.get(field);
               }
           }
           return yyyyMMddHHmmss.parseObject(dateString + timeString);
       } catch(ParseException e) {
           throw new RuntimeException(e);
       }
   }

}

XML Write Transformer(s)

An XML write transformer is responsible for constructing an XML value from the object. A transformation mapping may have multiple write transformers. For this example we will have two.

The first XML write transformer is responsible for writing out the year, month, and day information in the format yyMMdd.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

import java.text.SimpleDateFormat; import java.util.Date;

import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping; import org.eclipse.persistence.mappings.transformers.FieldTransformer; import org.eclipse.persistence.sessions.Session;

public class DateFieldTransformer implements FieldTransformer {

   private AbstractTransformationMapping mapping;
   private SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

   public void initialize(AbstractTransformationMapping mapping) {
       this.mapping = mapping;
   }

   public Object buildFieldValue(Object instance, String xPath, Session session) {
       Date date = (Date) mapping.getAttributeValueFromObject(instance);
       return yyyyMMdd.format(date);
   }

}

The second XML write transformer is responsible for writing out the hour, minute, and second information in the format HHmmss.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

import java.text.SimpleDateFormat; import java.util.Date;

import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping; import org.eclipse.persistence.mappings.transformers.FieldTransformer; import org.eclipse.persistence.sessions.Session;

public class TimeFieldTransformer implements FieldTransformer {

   private AbstractTransformationMapping mapping;
   private SimpleDateFormat HHmmss = new SimpleDateFormat("HHmmss");

   public void initialize(AbstractTransformationMapping mapping) {
       this.mapping = mapping;
   }

   public Object buildFieldValue(Object instance, String xPath, Session session) {
       Date date = (Date) mapping.getAttributeValueFromObject(instance);
       return HHmmss.format(date);
   }

}

jaxb.properties

In order to use the MOXy JAXB implementation you need to add a jaxb.properties file in which your model classes with the following entry:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

Demo

The following class can be used to demonstrate the mapping: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

import java.io.File;

import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller;

public class Demo {

   public static void main(String[] args) throws Exception {
       JAXBContext jc = JAXBContext.newInstance(ElemB.class);

       Unmarshaller unmarshaller = jc.createUnmarshaller();
       File xml = new File("input.xml");
       ElemB elemB = (ElemB) unmarshaller.unmarshal(xml);

       Marshaller marshaller = jc.createMarshaller();
       marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
       marshaller.marshal(elemB, System.out);
   }

} Please Note


Eclipselink-logo.gif
Version: 2.2.0 - DRAFT
Other versions...

Copyright © Eclipse Foundation, Inc. All Rights Reserved.