1 package com.fasterxml.jackson.dataformat.xml;
2
3 import java.io.IOException;
4
5 import javax.xml.stream.XMLInputFactory;
6 import javax.xml.stream.XMLOutputFactory;
7 import javax.xml.stream.XMLStreamReader;
8 import javax.xml.stream.XMLStreamWriter;
9
10 import com.fasterxml.jackson.core.*;
11 import com.fasterxml.jackson.core.type.TypeReference;
12 import com.fasterxml.jackson.databind.*;
13 import com.fasterxml.jackson.databind.cfg.MapperBuilder;
14 import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
15 import com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder;
16 import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
17 import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
18 import com.fasterxml.jackson.dataformat.xml.ser.XmlSerializerProvider;
19 import com.fasterxml.jackson.dataformat.xml.util.DefaultXmlPrettyPrinter;
20 import com.fasterxml.jackson.dataformat.xml.util.XmlRootNameLookup;
21
22 /**
23  * Customized {@link ObjectMapper} that will read and write XML instead of JSON,
24  * using XML-backed {@link com.fasterxml.jackson.core.JsonFactory}
25  * implementation ({@link XmlFactory}).
26  *<p>
27  * Mapper itself overrides some aspects of functionality to try to handle
28  * data binding aspects as similar to JAXB as possible.
29  *<p>
30  * Note that most of configuration should be done by pre-constructing
31  * {@link JacksonXmlModule} explicitly, instead of relying on default settings.
32  */

33 public class XmlMapper extends ObjectMapper
34 {
35     // as of 2.6
36     private static final long serialVersionUID = 1L;
37
38     /**
39      * Builder implementation for constructing {@link XmlMapper} instances.
40      *
41      * @since 2.10
42      */

43     public static class Builder extends MapperBuilder<XmlMapper, Builder>
44     {
45         public Builder(XmlMapper m) {
46             super(m);
47         }
48
49         public Builder enable(FromXmlParser.Feature... features)  {
50             for (FromXmlParser.Feature f : features) {
51                 _mapper.enable(f);
52             }
53             return this;
54         }
55
56         public Builder disable(FromXmlParser.Feature... features) {
57             for (FromXmlParser.Feature f : features) {
58                 _mapper.disable(f);
59             }
60             return this;
61         }
62
63         public Builder configure(FromXmlParser.Feature feature, boolean state)
64         {
65             if (state) {
66                 _mapper.enable(feature);
67             } else {
68                 _mapper.disable(feature);
69             }
70             return this;
71         }
72
73         public Builder enable(ToXmlGenerator.Feature... features) {
74             for (ToXmlGenerator.Feature f : features) {
75                 _mapper.enable(f);
76             }
77             return this;
78         }
79
80         public Builder disable(ToXmlGenerator.Feature... features) {
81             for (ToXmlGenerator.Feature f : features) {
82                 _mapper.disable(f);
83             }
84             return this;
85         }
86
87         public Builder configure(ToXmlGenerator.Feature feature, boolean state)
88         {
89             if (state) {
90                 _mapper.enable(feature);
91             } else {
92                 _mapper.disable(feature);
93             }
94             return this;
95         }
96         
97         public Builder nameForTextElement(String name) {
98             _mapper.setXMLTextElementName(name);
99             return this;
100         }
101
102         public Builder defaultUseWrapper(boolean state) {
103             _mapper.setDefaultUseWrapper(state);
104             return this;
105         }
106     }
107
108     protected final static JacksonXmlModule DEFAULT_XML_MODULE = new JacksonXmlModule();
109
110     protected final static DefaultXmlPrettyPrinter DEFAULT_XML_PRETTY_PRINTER = new DefaultXmlPrettyPrinter();
111
112     // need to hold on to module instance just in case copy() is used
113     protected final JacksonXmlModule _xmlModule;
114
115     /*
116     /**********************************************************
117     /* Life-cycle: construction, configuration
118     /**********************************************************
119      */

120
121     public XmlMapper() {
122         this(new XmlFactory());
123     }
124
125     /**
126      * @since 2.4
127      */

128     public XmlMapper(XMLInputFactory inputF, XMLOutputFactory outF) {
129         this(new XmlFactory(inputF, outF));
130     }
131
132     /**
133      * @since 2.4
134      */

135     public XmlMapper(XMLInputFactory inputF) {
136         this(new XmlFactory(inputF));
137     }
138
139     public XmlMapper(XmlFactory xmlFactory) {
140         this(xmlFactory, DEFAULT_XML_MODULE);
141     }
142
143     public XmlMapper(JacksonXmlModule module) {
144         this(new XmlFactory(), module);
145     }
146
147     public XmlMapper(XmlFactory xmlFactory, JacksonXmlModule module)
148     {
149         /* Need to override serializer provider (due to root name handling);
150          * deserializer provider fine as is
151          */

152         super(xmlFactory, new XmlSerializerProvider(new XmlRootNameLookup()), null);
153         _xmlModule = module;
154         // but all the rest is done via Module interface!
155         if (module != null) {
156             registerModule(module);
157         }
158         // 19-May-2015, tatu: Must ensure we use XML-specific indenter
159         _serializationConfig = _serializationConfig.withDefaultPrettyPrinter(DEFAULT_XML_PRETTY_PRINTER);
160         // 21-Jun-2017, tatu: Seems like there are many cases in XML where ability to coerce empty
161         //    String into `null` (where it otherwise is an error) is very useful.
162         enable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
163     }
164
165     /**
166      * @since 2.8.9
167      */

168     protected XmlMapper(XmlMapper src) {
169         super(src);
170         _xmlModule = src._xmlModule;
171     }
172
173     @Override
174     public XmlMapper copy()
175     {
176         _checkInvalidCopy(XmlMapper.class);
177         return new XmlMapper(this);
178     }
179
180     /**
181      * @since 2.10
182      */

183     public static XmlMapper.Builder xmlBuilder() {
184         return new XmlMapper.Builder(new XmlMapper());
185     }
186
187     /**
188      * @since 2.10
189      */

190     public static XmlMapper.Builder builder() {
191         return new XmlMapper.Builder(new XmlMapper());
192     }
193
194     /**
195      * @since 2.10
196      */

197     public static XmlMapper.Builder builder(XmlFactory streamFactory) {
198         return new XmlMapper.Builder(new XmlMapper(streamFactory));
199     }
200
201     @Override
202     public Version version() {
203         return PackageVersion.VERSION;
204     }
205
206     /*
207     /**********************************************************
208     /* Factory method overrides
209     /**********************************************************
210      */

211
212     @Override // since 2.10
213     protected TypeResolverBuilder<?> _constructDefaultTypeResolverBuilder(DefaultTyping applicability,
214             PolymorphicTypeValidator ptv) {
215         return new DefaultingXmlTypeResolverBuilder(applicability, ptv);
216     }
217
218     /*
219     /**********************************************************
220     /* Additional XML-specific configurations
221     /**********************************************************
222      */

223
224     /**
225      * Method called by {@link JacksonXmlModule} to pass configuration
226      * information to {@link XmlFactory}, during registration; NOT
227      * exposed as public method since configuration should be done
228      * via {@link JacksonXmlModule}.
229      * 
230      * @since 2.1
231      *
232      * @deprecated Since 2.10 use {@link Builder#nameForTextElement(String)} instead
233      */

234     @Deprecated
235     protected void setXMLTextElementName(String name) {
236         ((XmlFactory) _jsonFactory).setXMLTextElementName(name);
237     }
238
239     /**
240      * Since 2.7
241      * 
242      * @deprecated Since 2.10 use {@link Builder#defaultUseWrapper(boolean)} instead
243      */

244     @Deprecated
245     public XmlMapper setDefaultUseWrapper(boolean state) {
246         // ser and deser configs should usually have the same introspector, so:
247         AnnotationIntrospector ai0 = getDeserializationConfig().getAnnotationIntrospector();
248         for (AnnotationIntrospector ai : ai0.allIntrospectors()) {
249             if (ai instanceof XmlAnnotationIntrospector) {
250                 ((XmlAnnotationIntrospector) ai).setDefaultUseWrapper(state);
251             }
252         }
253         return this;
254     }
255
256     /*
257     /**********************************************************
258     /* Access to configuration settings
259     /**********************************************************
260      */

261
262     @Override
263     public XmlFactory getFactory() {
264         return (XmlFactory) _jsonFactory;
265     }
266     
267     public ObjectMapper configure(ToXmlGenerator.Feature f, boolean state) {
268         ((XmlFactory)_jsonFactory).configure(f, state);
269         return this;
270     }
271
272     public ObjectMapper configure(FromXmlParser.Feature f, boolean state) {
273         ((XmlFactory)_jsonFactory).configure(f, state);
274         return this;
275     }
276
277     public ObjectMapper enable(ToXmlGenerator.Feature f) {
278         ((XmlFactory)_jsonFactory).enable(f);
279         return this;
280     }
281
282     public ObjectMapper enable(FromXmlParser.Feature f) {
283         ((XmlFactory)_jsonFactory).enable(f);
284         return this;
285     }
286
287     public ObjectMapper disable(ToXmlGenerator.Feature f) {
288         ((XmlFactory)_jsonFactory).disable(f);
289         return this;
290     }
291
292     public ObjectMapper disable(FromXmlParser.Feature f) {
293         ((XmlFactory)_jsonFactory).disable(f);
294         return this;
295     }
296
297     /*
298     /**********************************************************
299     /* XML-specific access
300     /**********************************************************
301      */

302
303     /**
304      * Method for reading a single XML value from given XML-specific input
305      * source; useful for incremental data-binding, combining traversal using
306      * basic Stax {@link XMLStreamReader} with data-binding by Jackson.
307      * 
308      * @since 2.4
309      */

310     public <T> T readValue(XMLStreamReader r, Class<T> valueType) throws IOException {
311         return readValue(r, _typeFactory.constructType(valueType));
312     } 
313
314     /**
315      * Method for reading a single XML value from given XML-specific input
316      * source; useful for incremental data-binding, combining traversal using
317      * basic Stax {@link XMLStreamReader} with data-binding by Jackson.
318      * 
319      * @since 2.4
320      */

321     public <T> T readValue(XMLStreamReader r, TypeReference<T> valueTypeRef) throws IOException {
322         return readValue(r, _typeFactory.constructType(valueTypeRef));
323     } 
324
325     /**
326      * Method for reading a single XML value from given XML-specific input
327      * source; useful for incremental data-binding, combining traversal using
328      * basic Stax {@link XMLStreamReader} with data-binding by Jackson.
329      * 
330      * @since 2.4
331      */

332     @SuppressWarnings("resource")
333     public <T> T readValue(XMLStreamReader r, JavaType valueType) throws IOException
334     {
335         FromXmlParser p = getFactory().createParser(r);
336         return super.readValue(p,  valueType);
337     } 
338
339     /**
340      * Method for serializing given value using specific {@link XMLStreamReader}:
341      * useful when building large XML files by binding individual items, one at
342      * a time.
343      * 
344      * @since 2.4
345      */

346     public void writeValue(XMLStreamWriter w0, Object value) throws IOException {
347         @SuppressWarnings("resource")
348         ToXmlGenerator g = getFactory().createGenerator(w0);
349         super.writeValue(g, value);
350         // NOTE: above call should do flush(); and we should NOT close here.
351         // Finally, 'g' has no buffers to release.
352     }
353 }
354