1 package com.fasterxml.jackson.databind.ext;
2 
3 import java.io.IOException;
4 import java.util.*;
5 
6 import javax.xml.datatype.*;
7 import javax.xml.namespace.QName;
8 
9 import com.fasterxml.jackson.core.*;
10 
11 import com.fasterxml.jackson.databind.*;
12 import com.fasterxml.jackson.databind.deser.Deserializers;
13 import com.fasterxml.jackson.databind.deser.std.FromStringDeserializer;
14 
15 /**
16  * Container deserializers that handle "core" XML types: ones included in standard
17  * JDK 1.5. Types are directly needed by JAXB, but may be unavailable on some
18  * limited platforms; hence separate out from basic deserializer factory.
19  */
20 public class CoreXMLDeserializers extends Deserializers.Base
21 {
22     /**
23      * Data type factories are thread-safe after instantiation (and
24      * configuration, if any); and since instantion (esp. implementation
25      * introspection) can be expensive we better reuse the instance.
26      */
27     final static DatatypeFactory _dataTypeFactory;
28     static {
29         try {
30             _dataTypeFactory = DatatypeFactory.newInstance();
31         } catch (DatatypeConfigurationException e) {
32             throw new RuntimeException(e);
33         }
34     }
35 
36     @Override
findBeanDeserializer(JavaType type, DeserializationConfig config, BeanDescription beanDesc)37     public JsonDeserializer<?> findBeanDeserializer(JavaType type,
38         DeserializationConfig config, BeanDescription beanDesc)
39     {
40         Class<?> raw = type.getRawClass();
41         if (raw == QName.class) {
42             return new Std(raw, TYPE_QNAME);
43         }
44         if (raw == XMLGregorianCalendar.class) {
45             return new Std(raw, TYPE_G_CALENDAR);
46         }
47         if (raw == Duration.class) {
48             return new Std(raw, TYPE_DURATION);
49         }
50         return null;
51     }
52 
53     @Override // since 2.11
hasDeserializerFor(DeserializationConfig config, Class<?> valueType)54     public boolean hasDeserializerFor(DeserializationConfig config, Class<?> valueType) {
55         return (valueType == QName.class)
56                 || (valueType == XMLGregorianCalendar.class)
57                 || (valueType == Duration.class)
58                 ;
59     }
60 
61     /*
62     /**********************************************************
63     /* Concrete deserializers
64     /**********************************************************
65      */
66 
67     protected final static int TYPE_DURATION = 1;
68     protected final static int TYPE_G_CALENDAR = 2;
69     protected final static int TYPE_QNAME = 3;
70 
71     /**
72      * Combo-deserializer that supports deserialization of somewhat optional
73      * javax.xml types {@link QName}, {@link Duration} and {@link XMLGregorianCalendar}.
74      * Combined into a single class to eliminate bunch of one-off implementation
75      * classes, to reduce resulting jar size (mostly).
76      *
77      * @since 2.4
78      */
79     public static class Std extends FromStringDeserializer<Object>
80     {
81         private static final long serialVersionUID = 1L;
82 
83         protected final int _kind;
84 
Std(Class<?> raw, int kind)85         public Std(Class<?> raw, int kind) {
86             super(raw);
87             _kind = kind;
88         }
89 
90         @Override
deserialize(JsonParser p, DeserializationContext ctxt)91         public Object deserialize(JsonParser p, DeserializationContext ctxt)
92             throws IOException
93         {
94             // For most types, use super impl; but GregorianCalendar also allows
95             // integer value (timestamp), which needs separate handling
96             if (_kind == TYPE_G_CALENDAR) {
97                 if (p.hasToken(JsonToken.VALUE_NUMBER_INT)) {
98                     return _gregorianFromDate(ctxt, _parseDate(p, ctxt));
99                 }
100             }
101             return super.deserialize(p, ctxt);
102         }
103 
104         @Override
_deserialize(String value, DeserializationContext ctxt)105         protected Object _deserialize(String value, DeserializationContext ctxt)
106             throws IOException
107         {
108             switch (_kind) {
109             case TYPE_DURATION:
110                 return _dataTypeFactory.newDuration(value);
111             case TYPE_QNAME:
112                 return QName.valueOf(value);
113             case TYPE_G_CALENDAR:
114                 Date d;
115                 try {
116                     d = _parseDate(value, ctxt);
117                 }
118                 catch (JsonMappingException e) {
119                     // try to parse from native XML Schema 1.0 lexical representation String,
120                     // which includes time-only formats not handled by parseXMLGregorianCalendarFromJacksonFormat(...)
121                     return _dataTypeFactory.newXMLGregorianCalendar(value);
122                 }
123                 return _gregorianFromDate(ctxt, d);
124             }
125             throw new IllegalStateException();
126         }
127 
_gregorianFromDate(DeserializationContext ctxt, Date d)128         protected XMLGregorianCalendar _gregorianFromDate(DeserializationContext ctxt,
129                 Date d)
130         {
131             if (d == null) {
132                 return null;
133             }
134             GregorianCalendar calendar = new GregorianCalendar();
135             calendar.setTime(d);
136             TimeZone tz = ctxt.getTimeZone();
137             if (tz != null) {
138                 calendar.setTimeZone(tz);
139             }
140             return _dataTypeFactory.newXMLGregorianCalendar(calendar);
141         }
142     }
143 }
144