xiejun
2024-11-01 80b6cbfc9c861469146318d0b3dd5f8b8b525b8a
Source/BladeX-Tool/blade-core-tool/src/main/java/org/springblade/core/tool/utils/XmlUtil.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,299 @@
/*
 *      Copyright (c) 2018-2028, DreamLu All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  Neither the name of the dreamlu.net developer nor the names of its
 *  contributors may be used to endorse or promote products derived from
 *  this software without specific prior written permission.
 *  Author: DreamLu å¢æ˜¥æ¢¦ (596392912@qq.com)
 */
package org.springblade.core.tool.utils;
import org.springframework.lang.Nullable;
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 org.xml.sax.SAXException;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.HashMap;
import java.util.Map;
/**
 * xpath解析xml
 *
 * <pre>
 *     æ–‡æ¡£åœ°å€ï¼š
 *     http://www.w3school.com.cn/xpath/index.asp
 * </pre>
 *
 * @author L.cm
 */
public class XmlUtil {
   private final XPath path;
   private final Document doc;
   private XmlUtil(InputSource inputSource) throws ParserConfigurationException, SAXException, IOException {
      DocumentBuilderFactory dbf = getDocumentBuilderFactory();
      DocumentBuilder db = dbf.newDocumentBuilder();
      doc = db.parse(inputSource);
      path = getXPathFactory().newXPath();
   }
   /**
    * åˆ›å»ºå·¥å…·ç±»
    *
    * @param inputSource inputSource
    * @return XmlUtil
    */
   private static XmlUtil create(InputSource inputSource) {
      try {
         return new XmlUtil(inputSource);
      } catch (ParserConfigurationException | SAXException | IOException e) {
         throw Exceptions.unchecked(e);
      }
   }
   /**
    * è½¬æ¢å·¥å…·ç±»
    *
    * @param inputStream inputStream
    * @return XmlUtil
    */
   public static XmlUtil of(InputStream inputStream) {
      InputSource inputSource = new InputSource(inputStream);
      return create(inputSource);
   }
   /**
    * è½¬æ¢å·¥å…·ç±»
    *
    * @param xmlStr xmlStr
    * @return XmlUtil
    */
   public static XmlUtil of(String xmlStr) {
      StringReader sr = new StringReader(xmlStr.trim());
      InputSource inputSource = new InputSource(sr);
      XmlUtil xmlUtil = create(inputSource);
      IoUtil.closeQuietly(sr);
      return xmlUtil;
   }
   /**
    * è½¬æ¢è·¯å¾„
    *
    * @param expression è¡¨è¾¾å¼
    * @param item       å®žä½“
    * @param returnType è¿”回类型
    * @return Object
    */
   private Object evalXPath(String expression, @Nullable Object item, QName returnType) {
      item = null == item ? doc : item;
      try {
         return path.evaluate(expression, item, returnType);
      } catch (XPathExpressionException e) {
         throw Exceptions.unchecked(e);
      }
   }
   /**
    * èŽ·å–String
    *
    * @param expression è·¯å¾„
    * @return {String}
    */
   public String getString(String expression) {
      return (String) evalXPath(expression, null, XPathConstants.STRING);
   }
   /**
    * èŽ·å–Boolean
    *
    * @param expression è·¯å¾„
    * @return {String}
    */
   public Boolean getBoolean(String expression) {
      return (Boolean) evalXPath(expression, null, XPathConstants.BOOLEAN);
   }
   /**
    * èŽ·å–Number
    *
    * @param expression è·¯å¾„
    * @return {Number}
    */
   public Number getNumber(String expression) {
      return (Number) evalXPath(expression, null, XPathConstants.NUMBER);
   }
   /**
    * èŽ·å–æŸä¸ªèŠ‚ç‚¹
    *
    * @param expression è·¯å¾„
    * @return {Node}
    */
   public Node getNode(String expression) {
      return (Node) evalXPath(expression, null, XPathConstants.NODE);
   }
   /**
    * èŽ·å–å­èŠ‚ç‚¹
    *
    * @param expression è·¯å¾„
    * @return NodeList
    */
   public NodeList getNodeList(String expression) {
      return (NodeList) evalXPath(expression, null, XPathConstants.NODESET);
   }
   /**
    * èŽ·å–String
    *
    * @param node       èŠ‚ç‚¹
    * @param expression ç›¸å¯¹äºŽnode的路径
    * @return {String}
    */
   public String getString(Object node, String expression) {
      return (String) evalXPath(expression, node, XPathConstants.STRING);
   }
   /**
    * èŽ·å–
    *
    * @param node       èŠ‚ç‚¹
    * @param expression ç›¸å¯¹äºŽnode的路径
    * @return {String}
    */
   public Boolean getBoolean(Object node, String expression) {
      return (Boolean) evalXPath(expression, node, XPathConstants.BOOLEAN);
   }
   /**
    * èŽ·å–
    *
    * @param node       èŠ‚ç‚¹
    * @param expression ç›¸å¯¹äºŽnode的路径
    * @return {Number}
    */
   public Number getNumber(Object node, String expression) {
      return (Number) evalXPath(expression, node, XPathConstants.NUMBER);
   }
   /**
    * èŽ·å–æŸä¸ªèŠ‚ç‚¹
    *
    * @param node       èŠ‚ç‚¹
    * @param expression è·¯å¾„
    * @return {Node}
    */
   public Node getNode(Object node, String expression) {
      return (Node) evalXPath(expression, node, XPathConstants.NODE);
   }
   /**
    * èŽ·å–å­èŠ‚ç‚¹
    *
    * @param node       èŠ‚ç‚¹
    * @param expression ç›¸å¯¹äºŽnode的路径
    * @return NodeList
    */
   public NodeList getNodeList(Object node, String expression) {
      return (NodeList) evalXPath(expression, node, XPathConstants.NODESET);
   }
   /**
    * é’ˆå¯¹æ²¡æœ‰åµŒå¥—节点的简单处理
    *
    * @return map集合
    */
   public Map<String, String> toMap() {
      Element root = doc.getDocumentElement();
      Map<String, String> params = new HashMap<>(16);
      // å°†èŠ‚ç‚¹å°è£…æˆmap形式
      NodeList list = root.getChildNodes();
      for (int i = 0; i < list.getLength(); i++) {
         Node node = list.item(i);
         if (node instanceof Element) {
            params.put(node.getNodeName(), node.getTextContent());
         }
      }
      return params;
   }
   private static volatile boolean preventedXXE = false;
   private static DocumentBuilderFactory getDocumentBuilderFactory() throws ParserConfigurationException {
      DocumentBuilderFactory dbf = XmlUtil.XmlHelperHolder.documentBuilderFactory;
      if (!preventedXXE) {
         preventXXE(dbf);
      }
      return dbf;
   }
   /**
    * preventXXE
    *
    * @param dbf
    * @throws ParserConfigurationException
    */
   private static void preventXXE(DocumentBuilderFactory dbf) throws ParserConfigurationException {
      // 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
      dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", 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
      dbf.setFeature("http://xml.org/sax/features/external-general-entities", 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
      dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
      // Disable external DTDs as well
      dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
      // and these as well, per Timothy Morgan's 2014 paper: "XML Schema, DTD, and Entity Attacks"
      dbf.setXIncludeAware(false);
      dbf.setExpandEntityReferences(false);
      preventedXXE = true;
   }
   private static XPathFactory getXPathFactory() {
      return XmlUtil.XmlHelperHolder.xPathFactory;
   }
   /**
    * å†…部类单例
    */
   private static class XmlHelperHolder {
      private static DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
      private static XPathFactory xPathFactory = XPathFactory.newInstance();
   }
}