001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.xml; 014 015import javax.xml.stream.*; 016import javax.xml.stream.util.*; 017 018import org.apache.juneau.*; 019import org.apache.juneau.annotation.*; 020import org.apache.juneau.parser.*; 021 022/** 023 * Parses text generated by the {@link XmlSerializer} class back into a POJO model. 024 * 025 * <h5 class='topic'>Media types</h5> 026 * 027 * Handles <c>Content-Type</c> types: <bc>text/xml</bc> 028 * 029 * <h5 class='topic'>Description</h5> 030 * 031 * See the {@link XmlSerializer} class for a description of Juneau-generated XML. 032 */ 033@ConfigurableContext 034public class XmlParser extends ReaderParser { 035 036 //------------------------------------------------------------------------------------------------------------------- 037 // Configurable properties 038 //------------------------------------------------------------------------------------------------------------------- 039 040 static final String PREFIX = "XmlParser"; 041 042 /** 043 * Configuration property: XML event allocator. 044 * 045 * <h5 class='section'>Property:</h5> 046 * <ul> 047 * <li><b>Name:</b> <js>"XmlParser.eventAllocator.c"</js> 048 * <li><b>Data type:</b> <code>Class<? <jk>extends</jk> {@link XMLEventAllocator}></code> 049 * <li><b>Default:</b> <jk>null</jk> 050 * <li><b>Session property:</b> <jk>false</jk> 051 * <li><b>Methods:</b> 052 * <ul> 053 * <li class='jm'>{@link XmlParserBuilder#eventAllocator(XMLEventAllocator)} 054 * </ul> 055 * </ul> 056 * 057 * <h5 class='section'>Description:</h5> 058 * <p> 059 * Associates an {@link XMLEventAllocator} with this parser. 060 */ 061 public static final String XML_eventAllocator = PREFIX + ".eventAllocator.c"; 062 063 /** 064 * Configuration property: Preserve root element during generalized parsing. 065 * 066 * <h5 class='section'>Property:</h5> 067 * <ul> 068 * <li><b>Name:</b> <js>"XmlParser.preserveRootElement.b"</js> 069 * <li><b>Data type:</b> <c>Boolean</c> 070 * <li><b>Default:</b> <jk>false</jk> 071 * <li><b>Session property:</b> <jk>false</jk> 072 * <li><b>Methods:</b> 073 * <ul> 074 * <li class='jm'>{@link XmlParserBuilder#preserveRootElement(boolean)} 075 * <li class='jm'>{@link XmlParserBuilder#preserveRootElement()} 076 * </ul> 077 * </ul> 078 * 079 * <h5 class='section'>Description:</h5> 080 * <p> 081 * If <jk>true</jk>, when parsing into a generic {@link ObjectMap}, the map will contain a single entry whose key 082 * is the root element name. 083 * 084 * <h5 class='section'>Example:</h5> 085 * <p class='bcode w800'> 086 * <jc>// Parser with preserve-root-element.</jc> 087 * ReaderParser p1 = XmlParser 088 * .<jsm>create</jsm>() 089 * .preserveRootElement(<jk>true</jk>) 090 * .build(); 091 * 092 * <jc>// Parser without preserve-root-element (the default behavior).</jc> 093 * ReaderParser p2 = XmlParser 094 * .<jsm>create</jsm>() 095 * .preserveRootElement(<jk>false</jk>) 096 * .build(); 097 * 098 * String xml = <js>"<root><a>foobar</a></root>"</js>; 099 * 100 * <jc>// Produces: "{ root: { a:'foobar' }}"</jc> 101 * ObjectMap m1 = p1.parse(xml, ObjectMap.<jk>class</jk>); 102 * 103 * <jc>// Produces: "{ a:'foobar' }"</jc> 104 * ObjectMap m2 = p2.parse(xml, ObjectMap.<jk>class)</jk>; 105 * </p> 106 */ 107 public static final String XML_preserveRootElement = PREFIX + ".preserveRootElement.b"; 108 109 /** 110 * Configuration property: XML reporter. 111 * 112 * <h5 class='section'>Property:</h5> 113 * <ul> 114 * <li><b>Name:</b> <js>"XmlParser.reporter.c"</js> 115 * <li><b>Data type:</b> <code>Class<? <jk>extends</jk> {@link XMLReporter}></code> 116 * <li><b>Default:</b> <jk>null</jk> 117 * <li><b>Session property:</b> <jk>false</jk> 118 * <li><b>Methods:</b> 119 * <ul> 120 * <li class='jm'>{@link XmlParserBuilder#reporter(XMLReporter)} 121 * </ul> 122 * </ul> 123 * 124 * <h5 class='section'>Description:</h5> 125 * <p> 126 * Associates an {@link XMLReporter} with this parser. 127 * 128 * <ul class='notes'> 129 * <li> 130 * Reporters are not copied to new parsers during a clone. 131 * </ul> 132 */ 133 public static final String XML_reporter = PREFIX + ".reporter.c"; 134 135 /** 136 * Configuration property: XML resolver. 137 * 138 * <h5 class='section'>Property:</h5> 139 * <ul> 140 * <li><b>Name:</b> <js>"XmlParser.resolver.c"</js> 141 * <li><b>Data type:</b> <code>Class<? <jk>extends</jk> {@link XMLResolver}></code> 142 * <li><b>Default:</b> <jk>null</jk> 143 * <li><b>Session property:</b> <jk>false</jk> 144 * <li><b>Methods:</b> 145 * <ul> 146 * <li class='jm'>{@link XmlParserBuilder#resolver(XMLResolver)} 147 * </ul> 148 * </ul> 149 * 150 * <h5 class='section'>Description:</h5> 151 * <p> 152 * Associates an {@link XMLResolver} with this parser. 153 */ 154 public static final String XML_resolver = PREFIX + ".resolver.c"; 155 156 /** 157 * Configuration property: Enable validation. 158 * 159 * <h5 class='section'>Property:</h5> 160 * <ul> 161 * <li><b>Name:</b> <js>"XmlParser.validating.b"</js> 162 * <li><b>Data type:</b> <c>Boolean</c> 163 * <li><b>Default:</b> <jk>false</jk> 164 * <li><b>Session property:</b> <jk>false</jk> 165 * <li><b>Methods:</b> 166 * <ul> 167 * <li class='jm'>{@link XmlParserBuilder#validating(boolean)} 168 * <li class='jm'>{@link XmlParserBuilder#validating()} 169 * </ul> 170 * </ul> 171 * 172 * <h5 class='section'>Description:</h5> 173 * <p> 174 * If <jk>true</jk>, XML document will be validated. 175 * 176 * <p> 177 * See {@link XMLInputFactory#IS_VALIDATING} for more info. 178 */ 179 public static final String XML_validating = PREFIX + ".validating.b"; 180 181 182 //------------------------------------------------------------------------------------------------------------------- 183 // Predefined instances 184 //------------------------------------------------------------------------------------------------------------------- 185 186 /** Default parser, all default settings.*/ 187 public static final XmlParser DEFAULT = new XmlParser(PropertyStore.DEFAULT); 188 189 190 //------------------------------------------------------------------------------------------------------------------- 191 // Instance 192 //------------------------------------------------------------------------------------------------------------------- 193 194 private final boolean 195 validating, 196 preserveRootElement; 197 private final XMLReporter reporter; 198 private final XMLResolver resolver; 199 private final XMLEventAllocator eventAllocator; 200 201 /** 202 * Constructor. 203 * 204 * @param ps 205 * The property store containing all the settings for this object. 206 */ 207 public XmlParser(PropertyStore ps) { 208 this(ps, "text/xml", "application/xml"); 209 } 210 211 /** 212 * Constructor. 213 * 214 * @param ps 215 * The property store containing all the settings for this object. 216 * @param consumes 217 * The list of media types that this parser consumes (e.g. <js>"application/json"</js>, <js>"*​/json"</js>). 218 */ 219 public XmlParser(PropertyStore ps, String...consumes) { 220 super(ps, consumes); 221 validating = getBooleanProperty(XML_validating, false); 222 preserveRootElement = getBooleanProperty(XML_preserveRootElement, false); 223 reporter = getInstanceProperty(XML_reporter, XMLReporter.class, null); 224 resolver = getInstanceProperty(XML_resolver, XMLResolver.class, null); 225 eventAllocator = getInstanceProperty(XML_eventAllocator, XMLEventAllocator.class, null); 226 } 227 228 @Override /* Context */ 229 public XmlParserBuilder builder() { 230 return new XmlParserBuilder(getPropertyStore()); 231 } 232 233 /** 234 * Instantiates a new clean-slate {@link XmlParserBuilder} object. 235 * 236 * <p> 237 * This is equivalent to simply calling <code><jk>new</jk> XmlParserBuilder()</code>. 238 * 239 * <p> 240 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 241 * the settings of the object called on. 242 * 243 * @return A new {@link XmlParserBuilder} object. 244 */ 245 public static XmlParserBuilder create() { 246 return new XmlParserBuilder(); 247 } 248 249 @Override /* Parser */ 250 public XmlParserSession createSession() { 251 return createSession(createDefaultSessionArgs()); 252 } 253 254 @Override /* Parser */ 255 public XmlParserSession createSession(ParserSessionArgs args) { 256 return new XmlParserSession(this, args); 257 } 258 259 //----------------------------------------------------------------------------------------------------------------- 260 // Properties 261 //----------------------------------------------------------------------------------------------------------------- 262 263 /** 264 * Configuration property: XML event allocator. 265 * 266 * @see #XML_eventAllocator 267 * @return 268 * The {@link XMLEventAllocator} associated with this parser, or <jk>null</jk> if there isn't one. 269 */ 270 protected final XMLEventAllocator getEventAllocator() { 271 return eventAllocator; 272 } 273 274 /** 275 * Configuration property: Preserve root element during generalized parsing. 276 * 277 * @see #XML_preserveRootElement 278 * @return 279 * <jk>true</jk> if when parsing into a generic {@link ObjectMap}, the map will contain a single entry whose key 280 * is the root element name. 281 */ 282 protected final boolean isPreserveRootElement() { 283 return preserveRootElement; 284 } 285 286 /** 287 * Configuration property: XML reporter. 288 * 289 * @see #XML_reporter 290 * @return 291 * The {@link XMLReporter} associated with this parser, or <jk>null</jk> if there isn't one. 292 */ 293 protected final XMLReporter getReporter() { 294 return reporter; 295 } 296 297 /** 298 * Configuration property: XML resolver. 299 * 300 * @see #XML_resolver 301 * @return 302 * The {@link XMLResolver} associated with this parser, or <jk>null</jk> if there isn't one. 303 */ 304 protected final XMLResolver getResolver() { 305 return resolver; 306 } 307 308 /** 309 * Configuration property: Enable validation. 310 * 311 * @see #XML_validating 312 * @return 313 * <jk>true</jk> if XML document will be validated. 314 */ 315 protected final boolean isValidating() { 316 return validating; 317 } 318 319 //----------------------------------------------------------------------------------------------------------------- 320 // Other methods 321 //----------------------------------------------------------------------------------------------------------------- 322 323 @Override /* Context */ 324 public ObjectMap toMap() { 325 return super.toMap() 326 .append("XmlParser", new DefaultFilteringObjectMap() 327 .append("validating", validating) 328 .append("preserveRootElement", preserveRootElement) 329 .append("reporter", reporter) 330 .append("resolver", resolver) 331 .append("eventAllocator", eventAllocator) 332 ); 333 } 334}