View Javadoc
1   package com.foxinmy.weixin4j.xml;
2   
3   import java.io.ByteArrayInputStream;
4   import java.io.ByteArrayOutputStream;
5   import java.io.IOException;
6   import java.io.InputStream;
7   import java.io.OutputStream;
8   import java.io.StringReader;
9   import java.io.StringWriter;
10  import java.util.HashMap;
11  import java.util.Map;
12  import java.util.Map.Entry;
13  import java.util.concurrent.ConcurrentHashMap;
14  
15  import javax.xml.bind.JAXBContext;
16  import javax.xml.bind.JAXBElement;
17  import javax.xml.bind.JAXBException;
18  import javax.xml.bind.Marshaller;
19  import javax.xml.bind.Unmarshaller;
20  import javax.xml.bind.annotation.XmlRootElement;
21  import javax.xml.namespace.QName;
22  import javax.xml.parsers.SAXParserFactory;
23  import javax.xml.stream.XMLInputFactory;
24  import javax.xml.stream.XMLOutputFactory;
25  import javax.xml.stream.XMLStreamConstants;
26  import javax.xml.stream.XMLStreamException;
27  import javax.xml.stream.XMLStreamReader;
28  import javax.xml.stream.XMLStreamWriter;
29  import javax.xml.transform.Source;
30  import javax.xml.transform.sax.SAXSource;
31  
32  import org.xml.sax.InputSource;
33  import org.xml.sax.XMLReader;
34  
35  import com.alibaba.fastjson.JSONObject;
36  import com.foxinmy.weixin4j.util.Consts;
37  import com.foxinmy.weixin4j.util.StringUtil;
38  
39  /**
40   * XML 处理
41   * 
42   * @className XmlStream
43   * @author jinyu(foxinmy@gmail.com)
44   * @date 2015年6月2日
45   * @since JDK 1.6
46   * @see
47   */
48  public final class XmlStream {
49  	private final static String ROOT_ELEMENT_XML = "xml";
50  	private final static String XML_VERSION = "1.0";
51  	private final static ConcurrentHashMap<Class<?>, JAXBContext> jaxbContexts = new ConcurrentHashMap<Class<?>, JAXBContext>();
52  	private final static SAXParserFactory spf = SAXParserFactory.newInstance();
53  	static {
54  		try {
55  			// This is the PRIMARY defense. If DTDs (doctypes) are disallowed,
56  			// almost all XML entity attacks are prevented
57  			// Xerces 2 only -
58  			// http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
59  			spf.setFeature(
60  					"http://apache.org/xml/features/disallow-doctype-decl",
61  					true);
62  			// If you can't completely disable DTDs, then at least do the
63  			// following:
64  			// Xerces 1 -
65  			// http://xerces.apache.org/xerces-j/features.html#external-general-entities
66  			// Xerces 2 -
67  			// http://xerces.apache.org/xerces2-j/features.html#external-general-entities
68  			// JDK7+ - http://xml.org/sax/features/external-general-entities
69  			spf.setFeature(
70  					"http://xml.org/sax/features/external-general-entities",
71  					false);
72  			// Xerces 1 -
73  			// http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
74  			// Xerces 2 -
75  			// http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
76  			// JDK7+ - http://xml.org/sax/features/external-parameter-entities
77  			spf.setFeature(
78  					"http://xml.org/sax/features/external-parameter-entities",
79  					false);
80  			// Disable external DTDs as well
81  			spf.setFeature(
82  					"http://apache.org/xml/features/nonvalidating/load-external-dtd",
83  					false);
84  			// and these as well, per Timothy Morgan's 2014 paper:
85  			// "XML Schema, DTD, and Entity Attacks"
86  			spf.setXIncludeAware(false);
87  		} catch (Exception e) {
88  			;
89  		}
90  	}
91  
92  	/**
93  	 * Xml2Bean
94  	 * 
95  	 * @param content
96  	 *            xml内容
97  	 * @param clazz
98  	 *            bean类型
99  	 * @return
100 	 */
101 	@SuppressWarnings("unchecked")
102 	public static <T> T fromXML(InputStream content, Class<T> clazz) {
103 		JAXBContext jaxbContext = getJaxbContext(clazz);
104 		try {
105 			Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
106 			XMLReader reader = spf.newSAXParser().getXMLReader();
107 			reader.setFeature(
108 					"http://apache.org/xml/features/disallow-doctype-decl",
109 					true);
110 			reader.setFeature(
111 					"http://apache.org/xml/features/nonvalidating/load-external-dtd",
112 					false); // This may not be strictly required as DTDs
113 							// shouldn't be allowed at all, per previous line.
114 			reader.setFeature(
115 					"http://xml.org/sax/features/external-general-entities",
116 					false);
117 			reader.setFeature(
118 					"http://xml.org/sax/features/external-parameter-entities",
119 					false);
120 			Source source = new SAXSource(reader, new InputSource(content));
121 			XmlRootElement rootElement = clazz
122 					.getAnnotation(XmlRootElement.class);
123 			if (rootElement == null
124 					|| rootElement.name().equals(
125 							XmlRootElement.class.getMethod("name")
126 									.getDefaultValue().toString())) {
127 				JAXBElement<T> jaxbElement = unmarshaller.unmarshal(source,
128 						clazz);
129 				return jaxbElement.getValue();
130 			} else {
131 				return (T) unmarshaller.unmarshal(source);
132 			}
133 		} catch (Exception ex) {
134 			throw new RuntimeException("Could not unmarshaller class [" + clazz
135 					+ "]", ex);
136 		} finally {
137 			if (content != null) {
138 				try {
139 					content.close();
140 				} catch (IOException e) {
141 					;
142 				}
143 			}
144 		}
145 	}
146 
147 	/**
148 	 * Xml2Bean
149 	 * 
150 	 * @param content
151 	 *            xml内容
152 	 * @param clazz
153 	 *            bean类型
154 	 * @return
155 	 */
156 	public static <T> T fromXML(String content, Class<T> clazz) {
157 		return fromXML(
158 				new ByteArrayInputStream(content.getBytes(Consts.UTF_8)), clazz);
159 	}
160 
161 	/**
162 	 * map2xml
163 	 * 
164 	 * @param map
165 	 *            value无嵌套的map
166 	 * @return xml内容
167 	 */
168 	public static String map2xml(Map<String, String> map) {
169 		StringWriter sw = new StringWriter();
170 		try {
171 			XMLStreamWriter xw = XMLOutputFactory.newInstance()
172 					.createXMLStreamWriter(sw);
173 			xw.writeStartDocument(Consts.UTF_8.name(), XML_VERSION);
174 			xw.writeStartElement(ROOT_ELEMENT_XML);
175 			for (Entry<String, String> entry : map.entrySet()) {
176 				if (StringUtil.isBlank(entry.getValue())) {
177 					continue;
178 				}
179 				xw.writeStartElement(entry.getKey());
180 				xw.writeCData(entry.getValue());
181 				xw.writeEndElement();
182 			}
183 			xw.writeEndDocument();
184 			xw.flush();
185 			xw.close();
186 		} catch (XMLStreamException e) {
187 			throw new IllegalArgumentException(e);
188 		} finally {
189 			try {
190 				sw.close();
191 			} catch (IOException e) {
192 				;
193 			}
194 		}
195 		return sw.getBuffer().toString();
196 	}
197 
198 	/**
199 	 * map2xml
200 	 * 
201 	 * @param json
202 	 *            value无嵌套的json
203 	 * @return xml内容
204 	 */
205 	public static String map2xml(JSONObject json) {
206 		StringWriter sw = new StringWriter();
207 		try {
208 			XMLStreamWriter xw = XMLOutputFactory.newInstance()
209 					.createXMLStreamWriter(sw);
210 			xw.writeStartDocument(Consts.UTF_8.name(), XML_VERSION);
211 			xw.writeStartElement(ROOT_ELEMENT_XML);
212 			for (Entry<String, Object> entry : json.entrySet()) {
213 				if (StringUtil.isBlank(json.getString(entry.getKey()))) {
214 					continue;
215 				}
216 				xw.writeStartElement(entry.getKey());
217 				xw.writeCData(json.getString(entry.getKey()));
218 				xw.writeEndElement();
219 			}
220 			xw.writeEndDocument();
221 			xw.flush();
222 			xw.close();
223 		} catch (XMLStreamException e) {
224 			throw new IllegalArgumentException(e);
225 		} finally {
226 			try {
227 				sw.close();
228 			} catch (IOException e) {
229 				;
230 			}
231 		}
232 		return sw.getBuffer().toString();
233 	}
234 
235 	/**
236 	 * xml2map
237 	 * 
238 	 * @param content
239 	 *            无嵌套节点的xml内容
240 	 * @return map对象
241 	 */
242 	public static Map<String, String> xml2map(String content) {
243 		Map<String, String> map = new HashMap<String, String>();
244 		StringReader sr = new StringReader(content);
245 		try {
246 			XMLStreamReader xr = XMLInputFactory.newInstance()
247 					.createXMLStreamReader(sr);
248 			while (true) {
249 				int event = xr.next();
250 				if (event == XMLStreamConstants.END_DOCUMENT) {
251 					xr.close();
252 					break;
253 				} else if (event == XMLStreamConstants.START_ELEMENT) {
254 					String name = xr.getLocalName();
255 					while (true) {
256 						event = xr.next();
257 						if (event == XMLStreamConstants.START_ELEMENT) {
258 							name = xr.getLocalName();
259 						} else if (event == XMLStreamConstants.END_ELEMENT) {
260 							break;
261 						} else if (event == XMLStreamConstants.CHARACTERS) {
262 							String value = xr.getText();
263 							map.put(name, value);
264 						}
265 					}
266 				}
267 			}
268 		} catch (XMLStreamException e) {
269 			throw new IllegalArgumentException(e);
270 		} finally {
271 			sr.close();
272 		}
273 		return map;
274 	}
275 
276 	/**
277 	 * Bean2Xml
278 	 * 
279 	 * @param object
280 	 *            bean对象
281 	 * @return xml内容
282 	 */
283 	public static String toXML(Object object) {
284 		ByteArrayOutputStream os = new ByteArrayOutputStream();
285 		toXML(object, os);
286 		return StringUtil.newStringUtf8(os.toByteArray());
287 	}
288 
289 	/**
290 	 * Bean2Xml
291 	 * 
292 	 * @param t
293 	 *            bean对象
294 	 * @param os
295 	 *            输出流
296 	 */
297 	@SuppressWarnings("unchecked")
298 	public static <T> void toXML(T t, OutputStream os) {
299 		Class<T> clazz = (Class<T>) t.getClass();
300 		JAXBContext jaxbContext = getJaxbContext(clazz);
301 		try {
302 			Marshaller marshaller = jaxbContext.createMarshaller();
303 			marshaller.setProperty(Marshaller.JAXB_ENCODING,
304 					Consts.UTF_8.name());
305 			XmlRootElement rootElement = clazz
306 					.getAnnotation(XmlRootElement.class);
307 			if (rootElement == null
308 					|| rootElement.name().equals(
309 							XmlRootElement.class.getMethod("name")
310 									.getDefaultValue().toString())) {
311 				marshaller.marshal(new JAXBElement<T>(new QName(
312 						ROOT_ELEMENT_XML), clazz, t), os);
313 			} else {
314 				marshaller.marshal(t, os);
315 			}
316 		} catch (Exception ex) {
317 			throw new RuntimeException("Could not marshal class [" + clazz
318 					+ "] ", ex);
319 		} finally {
320 			if (os != null) {
321 				try {
322 					os.close();
323 				} catch (IOException e) {
324 					;
325 				}
326 			}
327 		}
328 	}
329 
330 	private static JAXBContext getJaxbContext(Class<?> clazz) {
331 		JAXBContext jaxbContext = jaxbContexts.get(clazz);
332 		if (jaxbContext == null) {
333 			try {
334 				jaxbContext = JAXBContext.newInstance(clazz);
335 				jaxbContexts.putIfAbsent(clazz, jaxbContext);
336 			} catch (JAXBException ex) {
337 				throw new RuntimeException(
338 						"Could not instantiate JAXBContext for class [" + clazz
339 								+ "] ", ex);
340 			}
341 		}
342 		return jaxbContext;
343 	}
344 }