javaBean實體轉換爲 xml (hutool)

實體 轉爲 xml 工具類

調用

        String reqXml = XmlUtils.toStrGF(實體, true, false);

package product.util;

import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

import cn.hutool.core.util.XmlUtil;

import com.sun.xml.bind.marshaller.CharacterEscapeHandler;

public class XmlUtils extends XmlUtil {
	
	/**
	 * xml字符串轉爲bean
	 * @param xml xml字符串
	 * @param clazz 需要轉成的對象
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T parseBean(String xml, Class<T> clazz) {
		try {
			JAXBContext context = JAXBContext.newInstance(clazz);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			return (T) unmarshaller.unmarshal(new StringReader(xml));
		} catch (JAXBException e) {
			e.printStackTrace();
			return null;
		}
	}
	/**
	 * 將對象轉爲xml字符串
	 * 編碼爲utf-8
	 * 不格式化
	 * 省略頭部聲明
	 * @param obj 待轉的對象
	 * @return
	 */
	public static String toStr(Object obj) {
		return toStr(obj, false, true);
	}
	/**
	 * 將對象轉爲xml字符串
	 * 編碼爲utf-8
	 * @param obj 待轉的對象
	 * @param isFormat 是否格式化
	 * @param isIgnoreHead 是否忽略頭部
	 * @return
	 */
	public static String toStr(Object obj, boolean isFormat, boolean isIgnoreHead) {
		try {
			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");// //編碼格式
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isFormat);// 是否格式化生成的xml串
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, isIgnoreHead);// 是否省略xm頭聲明信息
			
			// 不進行轉義字符的處理    
			marshaller.setProperty(CharacterEscapeHandler.class.getName(), 
					new CharacterEscapeHandler() {    
				@Override
				public void escape(char[] ch, int start, int length,
						boolean isAttVal, Writer writer) throws IOException {
					writer.write(ch, start, length);  							
				}    
			});
			
			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			return writer.toString();
		} catch (JAXBException e) {
			e.printStackTrace();
			return null;
		}
	}
	/**
	 * 將對象轉爲xml字符串
	 * 編碼爲utf-8
	 * 格式化
	 * 省略頭部聲明
	 * @param obj 待轉的對象
	 * @return
	 */
	public static String toPrettyStr(Object obj) {
		return toStr(obj, true, true);
	}


	/**
	 * 將對象轉爲xml字符串
	 * 編碼爲 GBK
	 * @param obj 待轉的對象
	 * @param isFormat 是否格式化
	 * @param isIgnoreHead 是否忽略頭部
	 * @return
	 */
	public static String toStrGF(Object obj, boolean isFormat, boolean isIgnoreHead) {
		try {
			JAXBContext context = JAXBContext.newInstance(obj.getClass());
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty(Marshaller.JAXB_ENCODING, "GBK");// //編碼格式
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, isFormat);// 是否格式化生成的xml串
			marshaller.setProperty(Marshaller.JAXB_FRAGMENT, isIgnoreHead);// 是否省略xm頭聲明信息

			// 不進行轉義字符的處理
			marshaller.setProperty(CharacterEscapeHandler.class.getName(),
					new CharacterEscapeHandler() {
						@Override
						public void escape(char[] ch, int start, int length,
										   boolean isAttVal, Writer writer) throws IOException {
							writer.write(ch, start, length);
						}
					});

			StringWriter writer = new StringWriter();
			marshaller.marshal(obj, writer);
			return writer.toString();
		} catch (JAXBException e) {
			e.printStackTrace();
			return null;
		}
	}
}

糊塗工具類 cn.hutool.core.util

package cn.hutool.core.util;

import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.BufferedInputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;

/**
 * XML工具類<br>
 * 此工具使用w3c dom工具,不需要依賴第三方包。<br>
 * 工具類封裝了XML文檔的創建、讀取、寫出和部分XML操作
 * 
 * @author xiaoleilu
 * 
 */
public class XmlUtil {

	/** 在XML中無效的字符 正則 */
	public final static String INVALID_REGEX = "[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f]";
	/** XML格式化輸出默認縮進量 */
	public final static int INDENT_DEFAULT = 2;

	// -------------------------------------------------------------------------------------- Read
	/**
	 * 讀取解析XML文件
	 * 
	 * @param file XML文件
	 * @return XML文檔對象
	 */
	public static Document readXML(File file) {
		Assert.notNull(file, "Xml file is null !");
		if (false == file.exists()) {
			throw new UtilException("File [{}] not a exist!", file.getAbsolutePath());
		}
		if (false == file.isFile()) {
			throw new UtilException("[{}] not a file!", file.getAbsolutePath());
		}

		try {
			file = file.getCanonicalFile();
		} catch (IOException e) {
			// ignore
		}

		BufferedInputStream in = null;
		try {
			in = FileUtil.getInputStream(file);
			return readXML(in);
		} finally {
			IoUtil.close(in);
		}
	}

	/**
	 * 讀取解析XML文件<br>
	 * 如果給定內容以“&lt;”開頭,表示這是一個XML內容,直接讀取,否則按照路徑處理<br>
	 * 路徑可以爲相對路徑,也可以是絕對路徑,相對路徑相對於ClassPath
	 * 
	 * @param pathOrContent 內容或路徑
	 * @return XML文檔對象
	 * @since 3.0.9
	 */
	public static Document readXML(String pathOrContent) {
		if (StrUtil.startWith(pathOrContent, '<')) {
			return parseXml(pathOrContent);
		}
		return readXML(FileUtil.file(pathOrContent));
	}

	/**
	 * 讀取解析XML文件<br>
	 * 編碼在XML中定義
	 * 
	 * @param inputStream XML流
	 * @return XML文檔對象
	 * @throws UtilException IO異常或轉換異常
	 * @since 3.0.9
	 */
	public static Document readXML(InputStream inputStream) throws UtilException {
		return readXML(new InputSource(inputStream));
	}

	/**
	 * 讀取解析XML文件
	 * 
	 * @param reader XML流
	 * @return XML文檔對象
	 * @throws UtilException IO異常或轉換異常
	 * @since 3.0.9
	 */
	public static Document readXML(Reader reader) throws UtilException {
		return readXML(new InputSource(reader));
	}

	/**
	 * 讀取解析XML文件<br>
	 * 編碼在XML中定義
	 * 
	 * @param source {@link InputSource}
	 * @return XML文檔對象
	 * @since 3.0.9
	 */
	public static Document readXML(InputSource source) {
		final DocumentBuilder builder = createDocumentBuilder();
		try {
			return builder.parse(source);
		} catch (Exception e) {
			throw new UtilException(e, "Parse XML from stream error!");
		}
	}

	/**
	 * 將String類型的XML轉換爲XML文檔
	 * 
	 * @param xmlStr XML字符串
	 * @return XML文檔
	 */
	public static Document parseXml(String xmlStr) {
		if (StrUtil.isBlank(xmlStr)) {
			throw new IllegalArgumentException("XML content string is empty !");
		}
		xmlStr = cleanInvalid(xmlStr);
		return readXML(new InputSource(StrUtil.getReader(xmlStr)));
	}

	/**
	 * 從XML中讀取對象 Reads serialized object from the XML file.
	 * 
	 * @param <T> 對象類型
	 * @param source XML文件
	 * @return 對象
	 * @throws IOException IO異常
	 */
	public static <T> T readObjectFromXml(File source) throws IOException {
		return readObjectFromXml(new InputSource(FileUtil.getInputStream(source)));
	}

	/**
	 * 從XML中讀取對象 Reads serialized object from the XML file.
	 * 
	 * @param <T> 對象類型
	 * @param xmlStr XML內容
	 * @return 對象
	 * @throws IOException IO異常
	 * @since 3.2.0
	 */
	public static <T> T readObjectFromXml(String xmlStr) throws IOException {
		return readObjectFromXml(new InputSource(StrUtil.getReader(xmlStr)));
	}

	/**
	 * 從XML中讀取對象 Reads serialized object from the XML file.
	 * 
	 * @param <T> 對象類型
	 * @param source {@link InputSource}
	 * @return 對象
	 * @throws IOException IO異常
	 * @since 3.2.0
	 */
	@SuppressWarnings("unchecked")
	public static <T> T readObjectFromXml(InputSource source) throws IOException {
		Object result = null;
		XMLDecoder xmldec = null;
		try {
			xmldec = new XMLDecoder(source);
			result = xmldec.readObject();
		} finally {
			IoUtil.close(xmldec);
		}
		return (T) result;
	}

	// -------------------------------------------------------------------------------------- Write
	/**
	 * 將XML文檔轉換爲String<br>
	 * 字符編碼使用XML文檔中的編碼,獲取不到則使用UTF-8<br>
	 * 默認非格式化輸出,若想格式化請使用{@link #format(Document)}
	 * 
	 * @param doc XML文檔
	 * @return XML字符串
	 */
	public static String toStr(Document doc) {
		return toStr(doc, false);
	}

	/**
	 * 將XML文檔轉換爲String<br>
	 * 字符編碼使用XML文檔中的編碼,獲取不到則使用UTF-8
	 * 
	 * @param doc XML文檔
	 * @param isPretty 是否格式化輸出
	 * @return XML字符串
	 * @since 3.0.9
	 */
	public static String toStr(Document doc, boolean isPretty) {
		return toStr(doc, CharsetUtil.UTF_8, isPretty);
	}

	/**
	 * 將XML文檔轉換爲String<br>
	 * 字符編碼使用XML文檔中的編碼,獲取不到則使用UTF-8
	 * 
	 * @param doc XML文檔
	 * @param charset 編碼
	 * @param isPretty 是否格式化輸出
	 * @return XML字符串
	 * @since 3.0.9
	 */
	public static String toStr(Document doc, String charset, boolean isPretty) {
		final StringWriter writer = StrUtil.getWriter();
		try {
			write(doc, writer, charset, isPretty ? INDENT_DEFAULT : 0);
		} catch (Exception e) {
			throw new UtilException(e, "Trans xml document to string error!");
		}
		return writer.toString();
	}

	/**
	 * 格式化XML輸出
	 * 
	 * @param doc {@link Document} XML文檔
	 * @return 格式化後的XML字符串
	 * @since 4.4.5
	 */
	public static String format(Document doc) {
		return toStr(doc, true);
	}

	/**
	 * 格式化XML輸出
	 * 
	 * @param xmlStr XML字符串
	 * @return 格式化後的XML字符串
	 * @since 4.4.5
	 */
	public static String format(String xmlStr) {
		return format(parseXml(xmlStr));
	}

	/**
	 * 將XML文檔寫入到文件<br>
	 * 使用Document中的編碼
	 * 
	 * @param doc XML文檔
	 * @param absolutePath 文件絕對路徑,不存在會自動創建
	 */
	public static void toFile(Document doc, String absolutePath) {
		toFile(doc, absolutePath, null);
	}

	/**
	 * 將XML文檔寫入到文件<br>
	 * 
	 * @param doc XML文檔
	 * @param path 文件路徑絕對路徑或相對ClassPath路徑,不存在會自動創建
	 * @param charset 自定義XML文件的編碼,如果爲{@code null} 讀取XML文檔中的編碼,否則默認UTF-8
	 */
	public static void toFile(Document doc, String path, String charset) {
		if (StrUtil.isBlank(charset)) {
			charset = doc.getXmlEncoding();
		}
		if (StrUtil.isBlank(charset)) {
			charset = CharsetUtil.UTF_8;
		}

		BufferedWriter writer = null;
		try {
			writer = FileUtil.getWriter(path, charset, false);
			write(doc, writer, charset, INDENT_DEFAULT);
		} finally {
			IoUtil.close(writer);
		}
	}

	/**
	 * 將XML文檔寫出
	 * 
	 * @param node {@link Node} XML文檔節點或文檔本身
	 * @param writer 寫出的Writer,Writer決定了輸出XML的編碼
	 * @param charset 編碼
	 * @param indent 格式化輸出中縮進量,小於1表示不格式化輸出
	 * @since 3.0.9
	 */
	public static void write(Node node, Writer writer, String charset, int indent) {
		transform(new DOMSource(node), new StreamResult(writer), charset, indent);
	}

	/**
	 * 將XML文檔寫出
	 * 
	 * @param node {@link Node} XML文檔節點或文檔本身
	 * @param out 寫出的Writer,Writer決定了輸出XML的編碼
	 * @param charset 編碼
	 * @param indent 格式化輸出中縮進量,小於1表示不格式化輸出
	 * @since 4.0.8
	 */
	public static void write(Node node, OutputStream out, String charset, int indent) {
		transform(new DOMSource(node), new StreamResult(out), charset, indent);
	}

	/**
	 * 將XML文檔寫出<br>
	 * 格式化輸出邏輯參考:https://stackoverflow.com/questions/139076/how-to-pretty-print-xml-from-java
	 * 
	 * @param source 源
	 * @param result 目標
	 * @param charset 編碼
	 * @param indent 格式化輸出中縮進量,小於1表示不格式化輸出
	 * @since 4.0.9
	 */
	public static void transform(Source source, Result result, String charset, int indent) {
		final TransformerFactory factory = TransformerFactory.newInstance();
		try {
			final Transformer xformer = factory.newTransformer();
			if (indent > 0) {
				xformer.setOutputProperty(OutputKeys.INDENT, "yes");
				xformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", String.valueOf(indent));
			}
			if (StrUtil.isNotBlank(charset)) {
				xformer.setOutputProperty(OutputKeys.ENCODING, charset);
			}
			xformer.transform(source, result);
		} catch (Exception e) {
			throw new UtilException(e, "Trans xml document to string error!");
		}
	}

	// -------------------------------------------------------------------------------------- Create
	/**
	 * 創建XML文檔<br>
	 * 創建的XML默認是utf8編碼,修改編碼的過程是在toStr和toFile方法裏,既XML在轉爲文本的時候才定義編碼
	 * 
	 * @return XML文檔
	 * @since 4.0.8
	 */
	public static Document createXml() {
		return createDocumentBuilder().newDocument();
	}

	/**
	 * 創建 DocumentBuilder
	 * 
	 * @return DocumentBuilder
	 * @since 4.1.2
	 */
	public static DocumentBuilder createDocumentBuilder() {
		final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		disableXXE(dbf);
		DocumentBuilder builder = null;
		try {
			builder = dbf.newDocumentBuilder();
		} catch (Exception e) {
			throw new UtilException(e, "Create xml document error!");
		}
		return builder;
	}

	/**
	 * 創建XML文檔<br>
	 * 創建的XML默認是utf8編碼,修改編碼的過程是在toStr和toFile方法裏,既XML在轉爲文本的時候才定義編碼
	 * 
	 * @param rootElementName 根節點名稱
	 * @return XML文檔
	 */
	public static Document createXml(String rootElementName) {
		final Document doc = createXml();
		doc.appendChild(doc.createElement(rootElementName));

		return doc;
	}

	// -------------------------------------------------------------------------------------- Function
	/**
	 * 獲得XML文檔根節點
	 * 
	 * @param doc {@link Document}
	 * @return 根節點
	 * @see Document#getDocumentElement()
	 * @since 3.0.8
	 */
	public static Element getRootElement(Document doc) {
		return (null == doc) ? null : doc.getDocumentElement();
	}

	/**
	 * 去除XML文本中的無效字符
	 * 
	 * @param xmlContent XML文本
	 * @return 當傳入爲null時返回null
	 */
	public static String cleanInvalid(String xmlContent) {
		if (xmlContent == null) {
			return null;
		}
		return xmlContent.replaceAll(INVALID_REGEX, "");
	}

	/**
	 * 根據節點名獲得子節點列表
	 * 
	 * @param element 節點
	 * @param tagName 節點名,如果節點名爲空(null或blank),返回所有子節點
	 * @return 節點列表
	 */
	public static List<Element> getElements(Element element, String tagName) {
		final NodeList nodeList = StrUtil.isBlank(tagName) ? element.getChildNodes() : element.getElementsByTagName(tagName);
		return transElements(element, nodeList);
	}

	/**
	 * 根據節點名獲得第一個子節點
	 * 
	 * @param element 節點
	 * @param tagName 節點名
	 * @return 節點
	 */
	public static Element getElement(Element element, String tagName) {
		final NodeList nodeList = element.getElementsByTagName(tagName);
		if (nodeList == null || nodeList.getLength() < 1) {
			return null;
		}
		int length = nodeList.getLength();
		for (int i = 0; i < length; i++) {
			Element childEle = (Element) nodeList.item(i);
			if (childEle == null || childEle.getParentNode() == element) {
				return childEle;
			}
		}
		return null;
	}

	/**
	 * 根據節點名獲得第一個子節點
	 * 
	 * @param element 節點
	 * @param tagName 節點名
	 * @return 節點中的值
	 */
	public static String elementText(Element element, String tagName) {
		Element child = getElement(element, tagName);
		return child == null ? null : child.getTextContent();
	}

	/**
	 * 根據節點名獲得第一個子節點
	 * 
	 * @param element 節點
	 * @param tagName 節點名
	 * @param defaultValue 默認值
	 * @return 節點中的值
	 */
	public static String elementText(Element element, String tagName, String defaultValue) {
		Element child = getElement(element, tagName);
		return child == null ? defaultValue : child.getTextContent();
	}

	/**
	 * 將NodeList轉換爲Element列表
	 * 
	 * @param nodeList NodeList
	 * @return Element列表
	 */
	public static List<Element> transElements(NodeList nodeList) {
		return transElements(null, nodeList);
	}

	/**
	 * 將NodeList轉換爲Element列表<br>
	 * 非Element節點將被忽略
	 * 
	 * @param parentEle 父節點,如果指定將返回此節點的所有直接子節點,nul返回所有就節點
	 * @param nodeList NodeList
	 * @return Element列表
	 */
	public static List<Element> transElements(Element parentEle, NodeList nodeList) {
		int length = nodeList.getLength();
		final ArrayList<Element> elements = new ArrayList<Element>(length);
		Node node;
		Element element;
		for (int i = 0; i < length; i++) {
			node = nodeList.item(i);
			if (Node.ELEMENT_NODE == node.getNodeType()) {
				element = (Element) nodeList.item(i);
				if (parentEle == null || element.getParentNode() == parentEle) {
					elements.add(element);
				}
			}
		}

		return elements;
	}

	/**
	 * 將可序列化的對象轉換爲XML寫入文件,已經存在的文件將被覆蓋<br>
	 * Writes serializable object to a XML file. Existing file will be overwritten
	 * 
	 * @param dest 目標文件
	 * @param bean 對象
	 * @throws IOException IO異常
	 */
	public static void writeObjectAsXml(File dest, Object bean) throws IOException {
		XMLEncoder xmlenc = null;
		try {
			xmlenc = new XMLEncoder(FileUtil.getOutputStream(dest));
			xmlenc.writeObject(bean);
		} finally {
			// 關閉XMLEncoder會相應關閉OutputStream
			IoUtil.close(xmlenc);
		}
	}

	/**
	 * 創建XPath<br>
	 * Xpath相關文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
	 * 
	 * @return {@link XPath}
	 * @since 3.2.0
	 */
	public static XPath createXPath() {
		return XPathFactory.newInstance().newXPath();
	}

	/**
	 * 通過XPath方式讀取XML節點等信息<br>
	 * Xpath相關文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
	 * 
	 * @param expression XPath表達式
	 * @param source 資源,可以是Docunent、Node節點等
	 * @return 匹配返回類型的值
	 * @since 4.0.9
	 */
	public static Element getElementByXPath(String expression, Object source) {
		return (Element) getNodeByXPath(expression, source);
	}

	/**
	 * 通過XPath方式讀取XML的NodeList<br>
	 * Xpath相關文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
	 * 
	 * @param expression XPath表達式
	 * @param source 資源,可以是Docunent、Node節點等
	 * @return NodeList
	 * @since 4.0.9
	 */
	public static NodeList getNodeListByXPath(String expression, Object source) {
		return (NodeList) getByXPath(expression, source, XPathConstants.NODESET);
	}

	/**
	 * 通過XPath方式讀取XML節點等信息<br>
	 * Xpath相關文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
	 * 
	 * @param expression XPath表達式
	 * @param source 資源,可以是Docunent、Node節點等
	 * @return 匹配返回類型的值
	 * @since 4.0.9
	 */
	public static Node getNodeByXPath(String expression, Object source) {
		return (Node) getByXPath(expression, source, XPathConstants.NODE);
	}

	/**
	 * 通過XPath方式讀取XML節點等信息<br>
	 * Xpath相關文章:https://www.ibm.com/developerworks/cn/xml/x-javaxpathapi.html
	 * 
	 * @param expression XPath表達式
	 * @param source 資源,可以是Docunent、Node節點等
	 * @param returnType 返回類型,{@link javax.xml.xpath.XPathConstants}
	 * @return 匹配返回類型的值
	 * @since 3.2.0
	 */
	public static Object getByXPath(String expression, Object source, QName returnType) {
		final XPath xPath = createXPath();
		try {
			if (source instanceof InputSource) {
				return xPath.evaluate(expression, (InputSource) source, returnType);
			} else {
				return xPath.evaluate(expression, source, returnType);
			}
		} catch (XPathExpressionException e) {
			throw new UtilException(e);
		}
	}

	/**
	 * 轉義XML特殊字符:
	 * 
	 * <pre>
	 * &amp; (ampersand) 替換爲 &amp;amp;
	 * &lt; (小於) 替換爲 &amp;lt;
	 * &gt; (大於) 替換爲 &amp;gt;
	 * &quot; (雙引號) 替換爲 &amp;quot;
	 * </pre>
	 * 
	 * @param string 被替換的字符串
	 * @return 替換後的字符串
	 * @since 4.0.8
	 */
	public static String escape(String string) {
		final StringBuilder sb = new StringBuilder(string.length());
		for (int i = 0, length = string.length(); i < length; i++) {
			char c = string.charAt(i);
			switch (c) {
			case '&':
				sb.append("&amp;");
				break;
			case '<':
				sb.append("&lt;");
				break;
			case '>':
				sb.append("&gt;");
				break;
			case '"':
				sb.append("&quot;");
				break;
			case '\'':
				sb.append("&apos;");
				break;
			default:
				sb.append(c);
			}
		}
		return sb.toString();
	}

	/**
	 * XML格式字符串轉換爲Map
	 *
	 * @param xmlStr XML字符串
	 * @return XML數據轉換後的Map
	 * @since 4.0.8
	 */
	public static Map<String, Object> xmlToMap(String xmlStr) {
		return xmlToMap(xmlStr, new HashMap<String, Object>());
	}

	/**
	 * XML格式字符串轉換爲Map
	 *
	 * @param node XML節點
	 * @return XML數據轉換後的Map
	 * @since 4.0.8
	 */
	public static Map<String, Object> xmlToMap(Node node) {
		return xmlToMap(node, new HashMap<String, Object>());
	}

	/**
	 * XML格式字符串轉換爲Map<br>
	 * 只支持第一級別的XML,不支持多級XML
	 *
	 * @param xmlStr XML字符串
	 * @param result 結果Map類型
	 * @return XML數據轉換後的Map
	 * @since 4.0.8
	 */
	public static Map<String, Object> xmlToMap(String xmlStr, Map<String, Object> result) {
		final Document doc = parseXml(xmlStr);
		final Element root = getRootElement(doc);
		root.normalize();

		return xmlToMap(root, result);
	}

	/**
	 * XML節點轉換爲Map
	 *
	 * @param node XML節點
	 * @param result 結果Map類型
	 * @return XML數據轉換後的Map
	 * @since 4.0.8
	 */
	public static Map<String, Object> xmlToMap(Node node, Map<String, Object> result) {
		if (null == result) {
			result = new HashMap<>();
		}

		final NodeList nodeList = node.getChildNodes();
		final int length = nodeList.getLength();
		Node childNode;
		Element childEle;
		for (int i = 0; i < length; ++i) {
			childNode = nodeList.item(i);
			if (isElement(childNode)) {
				childEle = (Element) childNode;
				result.put(childEle.getNodeName(), childEle.getTextContent());
			}
		}
		return result;
	}

	/**
	 * 將Map轉換爲XML格式的字符串
	 *
	 * @param data Map類型數據
	 * @return XML格式的字符串
	 * @since 4.0.8
	 */
	public static String mapToXmlStr(Map<?, ?> data, String rootName) {
		return toStr(mapToXml(data, rootName));
	}

	/**
	 * 將Map轉換爲XML
	 *
	 * @param data Map類型數據
	 * @return XML
	 * @since 4.0.9
	 */
	public static Document mapToXml(Map<?, ?> data, String rootName) {
		final Document doc = createXml();
		final Element root = appendChild(doc, rootName);

		mapToXml(doc, root, data);
		return doc;
	}

	/**
	 * 給定節點是否爲{@link Element} 類型節點
	 * 
	 * @param node 節點
	 * @return 是否爲{@link Element} 類型節點
	 * @since 4.0.8
	 */
	public static boolean isElement(Node node) {
		return (null == node) ? false : Node.ELEMENT_NODE == node.getNodeType();
	}

	/**
	 * 在已有節點上創建子節點
	 * 
	 * @param node 節點
	 * @param tagName 標籤名
	 * @return 子節點
	 * @since 4.0.9
	 */
	public static Element appendChild(Node node, String tagName) {
		Document doc = (node instanceof Document) ? (Document) node : node.getOwnerDocument();
		Element child = doc.createElement(tagName);
		node.appendChild(child);
		return child;
	}

	// ---------------------------------------------------------------------------------------- Private method start
	/**
	 * 將Map轉換爲XML格式的字符串
	 *
	 * @param doc {@link Document}
	 * @param element 節點
	 * @param data Map類型數據
	 * @since 4.0.8
	 */
	private static void mapToXml(Document doc, Element element, Map<?, ?> data) {
		Element filedEle;
		Object key;
		for (Entry<?, ?> entry : data.entrySet()) {
			key = entry.getKey();
			if (null != key) {
				// key作爲標籤名
				filedEle = doc.createElement(key.toString());
				element.appendChild(filedEle);
				final Object value = entry.getValue();
				// value作爲標籤內的值。
				if (null != value) {
					if (value instanceof Map) {
						// 如果值依舊爲map,遞歸繼續
						mapToXml(doc, filedEle, (Map<?, ?>) value);
						element.appendChild(filedEle);
					} else {
						filedEle.appendChild(doc.createTextNode(value.toString()));
					}
				}
			}
		}
	}

	/**
	 * 關閉XXE,避免漏洞攻擊<br>
	 * see: https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet#JAXP_DocumentBuilderFactory.2C_SAXParserFactory_and_DOM4J
	 * 
	 * @param dbf DocumentBuilderFactory
	 * @return DocumentBuilderFactory
	 */
	private static DocumentBuilderFactory disableXXE(DocumentBuilderFactory dbf) {
		String feature;
		try {
			// This is the PRIMARY defense. If DTDs (doctypes) are disallowed, almost all XML entity attacks are prevented
			// Xerces 2 only - http://xerces.apache.org/xerces2-j/features.html#disallow-doctype-decl
			feature = "http://apache.org/xml/features/disallow-doctype-decl";
			dbf.setFeature(feature, true);
			// If you can't completely disable DTDs, then at least do the following:
			// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-general-entities
			// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-general-entities
			// JDK7+ - http://xml.org/sax/features/external-general-entities
			feature = "http://xml.org/sax/features/external-general-entities";
			dbf.setFeature(feature, false);
			// Xerces 1 - http://xerces.apache.org/xerces-j/features.html#external-parameter-entities
			// Xerces 2 - http://xerces.apache.org/xerces2-j/features.html#external-parameter-entities
			// JDK7+ - http://xml.org/sax/features/external-parameter-entities
			feature = "http://xml.org/sax/features/external-parameter-entities";
			dbf.setFeature(feature, false);
			// Disable external DTDs as well
			feature = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
			dbf.setFeature(feature, false);
			// and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
			dbf.setXIncludeAware(false);
			dbf.setExpandEntityReferences(false);
		} catch (ParserConfigurationException e) {
			// ignore
		}
		return dbf;
	}
	// ---------------------------------------------------------------------------------------- Private method end

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章