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.parser.*; 020 021/** 022 * Parses text generated by the {@link XmlSerializer} class back into a POJO model. 023 * 024 * <h5 class='topic'>Media types</h5> 025 * 026 * Handles <code>Content-Type</code> types: <code><b>text/xml</b></code> 027 * 028 * <h5 class='topic'>Description</h5> 029 * 030 * See the {@link XmlSerializer} class for a description of Juneau-generated XML. 031 */ 032public class XmlParser extends ReaderParser { 033 034 //------------------------------------------------------------------------------------------------------------------- 035 // Configurable properties 036 //------------------------------------------------------------------------------------------------------------------- 037 038 private static final String PREFIX = "XmlParser."; 039 040 /** 041 * Configuration property: XML event allocator. 042 * 043 * <h5 class='section'>Property:</h5> 044 * <ul> 045 * <li><b>Name:</b> <js>"XmlParser.eventAllocator.c"</js> 046 * <li><b>Data type:</b> <code>Class<? <jk>extends</jk> {@link XMLEventAllocator}></code> 047 * <li><b>Default:</b> <jk>null</jk> 048 * <li><b>Session-overridable:</b> <jk>true</jk> 049 * <li><b>Methods:</b> 050 * <ul> 051 * <li class='jm'>{@link XmlParserBuilder#eventAllocator(XMLEventAllocator)} 052 * </ul> 053 * </ul> 054 * 055 * <h5 class='section'>Description:</h5> 056 * <p> 057 * Associates an {@link XMLEventAllocator} with this parser. 058 */ 059 public static final String XML_eventAllocator = PREFIX + "eventAllocator.c"; 060 061 /** 062 * Configuration property: Preserve root element during generalized parsing. 063 * 064 * <h5 class='section'>Property:</h5> 065 * <ul> 066 * <li><b>Name:</b> <js>"XmlParser.preserveRootElement.b"</js> 067 * <li><b>Data type:</b> <code>Boolean</code> 068 * <li><b>Default:</b> <jk>false</jk> 069 * <li><b>Session-overridable:</b> <jk>true</jk> 070 * <li><b>Methods:</b> 071 * <ul> 072 * <li class='jm'>{@link XmlParserBuilder#preserveRootElement(boolean)} 073 * <li class='jm'>{@link XmlParserBuilder#preserveRootElement()} 074 * </ul> 075 * </ul> 076 * 077 * <h5 class='section'>Description:</h5> 078 * <p> 079 * If <jk>true</jk>, when parsing into a generic {@link ObjectMap}, the map will contain a single entry whose key 080 * is the root element name. 081 * 082 * <h5 class='section'>Example:</h5> 083 * <p class='bcode'> 084 * <jc>// Parser with preserve-root-element.</jc> 085 * ReaderParser p1 = XmlParser 086 * .<jsm>create</jsm>() 087 * .preserveRootElement(<jk>true</jk>) 088 * .build(); 089 * 090 * <jc>// Parser without preserve-root-element (the default behavior).</jc> 091 * ReaderParser p2 = XmlParser 092 * .<jsm>create</jsm>() 093 * .preserveRootElement(<jk>false</jk>) 094 * .build(); 095 * 096 * String xml = <js>"<root><a>foobar</a></root>"</js>; 097 * 098 * <jc>// Produces: "{ root: { a:'foobar' }}"</jc> 099 * ObjectMap m1 = p1.parse(xml, ObjectMap.<jk>class</jk>); 100 * 101 * <jc>// Produces: "{ a:'foobar' }"</jc> 102 * ObjectMap m2 = p2.parse(xml, ObjectMap.<jk>class)</jk>; 103 * </p> 104 */ 105 public static final String XML_preserveRootElement = PREFIX + "preserveRootElement.b"; 106 107 /** 108 * Configuration property: XML reporter. 109 * 110 * <h5 class='section'>Property:</h5> 111 * <ul> 112 * <li><b>Name:</b> <js>"XmlParser.reporter.c"</js> 113 * <li><b>Data type:</b> <code>Class<? <jk>extends</jk> {@link XMLReporter}></code> 114 * <li><b>Default:</b> <jk>null</jk> 115 * <li><b>Session-overridable:</b> <jk>true</jk> 116 * <li><b>Methods:</b> 117 * <ul> 118 * <li class='jm'>{@link XmlParserBuilder#reporter(XMLReporter)} 119 * </ul> 120 * </ul> 121 * 122 * <h5 class='section'>Description:</h5> 123 * <p> 124 * Associates an {@link XMLReporter} with this parser. 125 * 126 * <h5 class='section'>Notes:</h5> 127 * <ul class='spaced-list'> 128 * <li> 129 * Reporters are not copied to new parsers during a clone. 130 * </ul> 131 */ 132 public static final String XML_reporter = PREFIX + "reporter.c"; 133 134 /** 135 * Configuration property: XML resolver. 136 * 137 * <h5 class='section'>Property:</h5> 138 * <ul> 139 * <li><b>Name:</b> <js>"XmlParser.resolver.c"</js> 140 * <li><b>Data type:</b> <code>Class<? <jk>extends</jk> {@link XMLResolver}></code> 141 * <li><b>Default:</b> <jk>null</jk> 142 * <li><b>Session-overridable:</b> <jk>true</jk> 143 * <li><b>Methods:</b> 144 * <ul> 145 * <li class='jm'>{@link XmlParserBuilder#resolver(XMLResolver)} 146 * </ul> 147 * </ul> 148 * 149 * <h5 class='section'>Description:</h5> 150 * <p> 151 * Associates an {@link XMLResolver} with this parser. 152 */ 153 public static final String XML_resolver = PREFIX + "resolver.c"; 154 155 /** 156 * Configuration property: Enable validation. 157 * 158 * <h5 class='section'>Property:</h5> 159 * <ul> 160 * <li><b>Name:</b> <js>"XmlParser.validating.b"</js> 161 * <li><b>Data type:</b> <code>Boolean</code> 162 * <li><b>Default:</b> <jk>false</jk> 163 * <li><b>Session-overridable:</b> <jk>true</jk> 164 * <li><b>Methods:</b> 165 * <ul> 166 * <li class='jm'>{@link XmlParserBuilder#validating(boolean)} 167 * <li class='jm'>{@link XmlParserBuilder#validating()} 168 * </ul> 169 * </ul> 170 * 171 * <h5 class='section'>Description:</h5> 172 * <p> 173 * If <jk>true</jk>, XML document will be validated. 174 * 175 * <p> 176 * See {@link XMLInputFactory#IS_VALIDATING} for more info. 177 */ 178 public static final String XML_validating = PREFIX + "validating.b"; 179 180 181 //------------------------------------------------------------------------------------------------------------------- 182 // Predefined instances 183 //------------------------------------------------------------------------------------------------------------------- 184 185 /** Default parser, all default settings.*/ 186 public static final XmlParser DEFAULT = new XmlParser(PropertyStore.DEFAULT); 187 188 189 //------------------------------------------------------------------------------------------------------------------- 190 // Instance 191 //------------------------------------------------------------------------------------------------------------------- 192 193 final boolean 194 validating, 195 preserveRootElement; 196 final XMLReporter reporter; 197 final XMLResolver resolver; 198 final XMLEventAllocator eventAllocator; 199 200 /** 201 * Constructor. 202 * 203 * @param ps 204 * The property store containing all the settings for this object. 205 */ 206 public XmlParser(PropertyStore ps) { 207 this(ps, "text/xml", "application/xml"); 208 } 209 210 /** 211 * Constructor. 212 * 213 * @param ps 214 * The property store containing all the settings for this object. 215 * @param consumes 216 * The list of media types that this parser consumes (e.g. <js>"application/json"</js>, <js>"*​/json"</js>). 217 */ 218 public XmlParser(PropertyStore ps, String...consumes) { 219 super(ps, consumes); 220 validating = getBooleanProperty(XML_validating, false); 221 preserveRootElement = getBooleanProperty(XML_preserveRootElement, false); 222 reporter = getInstanceProperty(XML_reporter, XMLReporter.class, null); 223 resolver = getInstanceProperty(XML_resolver, XMLResolver.class, null); 224 eventAllocator = getInstanceProperty(XML_eventAllocator, XMLEventAllocator.class, null); 225 } 226 227 @Override /* Context */ 228 public XmlParserBuilder builder() { 229 return new XmlParserBuilder(getPropertyStore()); 230 } 231 232 /** 233 * Instantiates a new clean-slate {@link XmlParserBuilder} object. 234 * 235 * <p> 236 * This is equivalent to simply calling <code><jk>new</jk> XmlParserBuilder()</code>. 237 * 238 * <p> 239 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 240 * the settings of the object called on. 241 * 242 * @return A new {@link XmlParserBuilder} object. 243 */ 244 public static XmlParserBuilder create() { 245 return new XmlParserBuilder(); 246 } 247 248 @Override /* Parser */ 249 public ReaderParserSession createSession(ParserSessionArgs args) { 250 return new XmlParserSession(this, args); 251 } 252 253 @Override /* Context */ 254 public ObjectMap asMap() { 255 return super.asMap() 256 .append("XmlParser", new ObjectMap() 257 .append("validating", validating) 258 .append("preserveRootElement", preserveRootElement) 259 .append("reporter", reporter) 260 .append("resolver", resolver) 261 .append("eventAllocator", eventAllocator) 262 ); 263 } 264}