com.feilong.xml.XmlUtil Maven / Gradle / Ivy
Show all versions of feilong Show documentation
/*
* Copyright (C) 2008 feilong
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.feilong.xml;
import static com.feilong.core.Validator.isNullOrEmpty;
import static com.feilong.core.lang.StringUtil.EMPTY;
import static com.feilong.core.util.MapUtil.newLinkedHashMap;
import static com.feilong.formatter.FormatterUtil.formatToSimpleTable;
import static java.util.Collections.emptyMap;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPathConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.feilong.core.Validate;
import com.feilong.json.JsonUtil;
import com.feilong.lib.lang3.StringUtils;
import com.feilong.lib.xstream.XStream;
import com.feilong.xml.xstream.XStreamConfig;
import com.feilong.xml.xstream.XStreamConfigBuilder;
import com.feilong.xml.xstream.converters.SimpleMapConverter;
/**
* xml 工具.
*
*
* Convenience methods for working with the DOM API, in particular for working with DOM Nodes and DOM Elements.
*
*
* 将object转成xml字符串.
*
* 使用示例:
*
*
*
* 对于将下面的对象,转成XML
* User user = new User(1L);
*
*
* 1.不带XStreamConfig参数
* 使用 {@code com.feilong.xml.XmlUtil.toXML(user, null)},则返回
*
*
* {@code
*
* feilong
* 1
*
*
*
* }
*
*
*
*
*
*
*
* 2.设置 Alias 参数
* 可以看到上面的结果中,XML Root元素名字是 com.feilong.test.User,如果只是显示成 {@code }怎么做呢?
* 使用
*
*
*
* User user = new User(1L);
* XStreamConfig xStreamConfig = new XStreamConfig();
* xStreamConfig.getAliasMap().put("user", User.class);
* LOGGER.info(XmlUtil.toXML(user, xStreamConfig));
*
*
*
* ,则返回
*
*
* {@code
*
* feilong
* 1
*
*
*
* }
*
*
*
*
*
* 3.设置 ImplicitCollection 参数
* 如果我在结果不想出现 {@code }怎么做呢?
* 使用
*
*
*
* User user = new User(1L);
* XStreamConfig xStreamConfig = new XStreamConfig();
* xStreamConfig.getAliasMap().put("user", User.class);
* xStreamConfig.getImplicitCollectionMap().put("userAddresseList", User.class);
* LOGGER.info(XmlUtil.toXML(user, xStreamConfig));
*
*
*
* ,则返回
*
*
* {@code
*
* feilong
* 1
*
*
* }
*
*
*
*
*
* @author feilong
* @author feilong
* @author feilong
* @see Frequently Asked Questions
* @see News
* @see "org.apache.solr.common.util.DOMUtil"
* @see "org.springframework.util.xml.DomUtils"
* @since 3.0.0
*/
public class XmlUtil{
/** The Constant LOGGER. */
private static final Logger LOGGER = LoggerFactory.getLogger(XmlUtil.class);
/** Don't let anyone instantiate this class. */
private XmlUtil(){
//AssertionError不是必须的. 但它可以避免不小心在类的内部调用构造器. 保证该类在任何情况下都不会被实例化.
//see 《Effective Java》 2nd
throw new AssertionError("No " + getClass().getName() + " instances for you!");
}
//---------------------------------------------------------------
/**
* 根据xpath表达式, 来获得节点的名字和string value map.
*
*
* 按照顺序返回
*
*
* @param xml
* the xml
* @param xpathExpression
* the xpath expression
* @return 如果 xml
是null,抛出 {@link NullPointerException}
* @since 3.0.0
*/
public static Map getNodeNameAndStringValueMap(Object xml,String xpathExpression){
return getNodeMap(
xml,
xpathExpression, //
(map,node) -> map.put(node.getNodeName(), node.getTextContent()));
}
/**
* 获得 node attribute value and string value map.
*
* @param xml
* the xml
* @param xpathExpression
* the xpath expression
* @param nodeAttributeName
* the node attribute name
* @return 如果 xml
是null,抛出 {@link NullPointerException}
* @since 3.0.0
*/
public static Map getNodeAttributeValueAndStringValueMap(
Object xml,
String xpathExpression,
final String nodeAttributeName){
return getNodeMap(
xml,
xpathExpression, //
(map,node) -> map.put(getAttributeValue(node, nodeAttributeName), node.getTextContent()));
}
/**
* Gets the node map.
*
* @param xml
* the xml
* @param xpathExpression
* the xpath expression
* @param hook
* the hook
* @return 如果 xml
是null,抛出 {@link NullPointerException}
*/
private static Map getNodeMap(Object xml,String xpathExpression,Hook hook){
Document document = FeilongDocumentBuilder.buildDocument(xml);
NodeList nodeList = XPathUtil.evaluate(document, xpathExpression, XPathConstants.NODESET);
if (null == nodeList || 0 == nodeList.getLength()){
if (LOGGER.isInfoEnabled()){
LOGGER.info("use xpathExpression:[{}],from xml:[{}], can not find Node,return emptyMap", xpathExpression, format(document));
}
return emptyMap();
}
//---------------------------------------------------------------
Map map = newLinkedHashMap();
for (int i = 0, j = nodeList.getLength(); i < j; ++i){
Node node = nodeList.item(i);
if (Node.ELEMENT_NODE == node.getNodeType()){
hook.hook(map, node);
}
}
//---------------------------------------------------------------
if (LOGGER.isTraceEnabled()){
LOGGER.trace("nameAndValueMap:{}", formatToSimpleTable(map));
}
return map;
}
//---------------------------------------------------------------
/**
* 获得node属性值.
*
* @param node
* 节点
* @param attributeName
* 属性名称
* @return 获得node属性值
* 如果 attributeName
是null,抛出 {@link NullPointerException}
* 如果 attributeName
是blank,抛出 {@link IllegalArgumentException}
*/
private static String getAttributeValue(Node node,String attributeName){
Validate.notNull(node, "node can't be null!");
Validate.notBlank(attributeName, "attributeName can't be null/empty!");
//---------------------------------------------------------------
NamedNodeMap namedNodeMap = node.getAttributes();
Node currentNode = namedNodeMap.getNamedItem(attributeName);
if (null != currentNode){
return currentNode.getNodeValue();
}
return EMPTY;
}
//---------------------------------------------------------------
/**
* Format.
*
* @param xml
* the xml
* @return the string
* @since 3.0.0
*/
public static String format(String xml){
Node node = FeilongDocumentBuilder.buildDocument(xml);
return format(node);
}
/**
* Format.
*
* @param node
* the node
* @return the string
* @throws UncheckedXmlParseException
* the unchecked xml parse exception
* @since 3.0.0
*/
private static String format(Node node){
try{
Writer writer = new StringWriter();
StreamResult streamResult = new StreamResult(writer);
TransformerBuilder.DEFAULT_TRANSFORMER.transform(//
new DOMSource(node),
streamResult);
return writer.toString();
}catch (TransformerException e){
throw new UncheckedXmlParseException(e);
}
}
//---------------------------------------------------------------
/**
* 将 map 转成 xml string.
*
* 示例:
*
*
*
*
* Map{@code } map = new HashMap{@code <>}();
* map.put("out_trade_no", "112122212");
* map.put("total_fee", "125.00");
* map.put("call_back_url", "");
* map.put("notify_url", "");
*
* LOGGER.debug(XmlUtil.toXML(map, "xml"));
*
*
* 返回:
*
*
* {@code
*
*
* 125.00
*
* 112122212
*
* }
*
*
*
*
* @param
* the key type
* @param
* the value type
* @param map
* the map
* @param rootElementName
* 根元素名字
* @return 如果 rootName
是null,抛出 {@link NullPointerException}
* 如果 rootName
是blank,抛出 {@link IllegalArgumentException}
* 如果 map
是null,抛出 {@link NullPointerException}
* 如果 map
是empty,抛出 {@link IllegalArgumentException}
* 如果 map
有 null key,将抛出 异常
* 如果 map
有 null value,将转成 {@link StringUtils#EMPTY}代替
*
* @see SimpleMapConverter
* @since 1.10.7
*/
public static String toXML(Map map,String rootElementName){
return toXML(map, rootElementName, true);
}
/**
* 将 map 转成 xml 字符串.
*
* 示例:
*
*
*
*
* Map{@code } map = new HashMap{@code <>}();
* map.put("out_trade_no", "112122212");
* map.put("total_fee", "125.00");
* map.put("call_back_url", "");
* map.put("notify_url", "");
*
* LOGGER.debug(XmlUtil.toXML(map, "xml",true));
*
*
* 返回:
*
*
* {@code
*
*
* 125.00
*
* 112122212
*
* }
*
*
* 如果此时,使用 isPrettyPrint
为 false 参数
*
*
* Map{@code } map = new HashMap{@code <>}();
* map.put("out_trade_no", "112122212");
* map.put("total_fee", "125.00");
* map.put("call_back_url", "");
* map.put("notify_url", "");
*
* LOGGER.debug(XmlUtil.toXML(map, "xml",false));
*
*
* 返回:
*
*
* {@code
125.00 112122212
* }
*
*
*
*
* @param
* the key type
* @param
* the value type
* @param map
* the map
* @param rootElementName
* 根元素名字
* @param isPrettyPrint
* 是否格式化输出
* @return 如果 rootName
是null,抛出 {@link NullPointerException}
* 如果 rootName
是blank,抛出 {@link IllegalArgumentException}
* 如果 map
是null,抛出 {@link NullPointerException}
* 如果 map
是empty,抛出 {@link IllegalArgumentException}
* 如果 map
有 null key,将抛出 异常
* 如果 map
有 null value,将转成 {@link StringUtils#EMPTY}代替
* @see SimpleMapConverter
*/
public static String toXML(Map map,String rootElementName,boolean isPrettyPrint){
Validate.notEmpty(map, "map can't be null/empty!");
Validate.notBlank(rootElementName, "rootName can't be blank!");
XStreamConfig xStreamConfig = XStreamConfigBuilder.buildSimpleMapXStreamConfig(rootElementName, isPrettyPrint);
// xStreamConfig.getDefaultImplementationMap().put(map.getClass(), Map.class)
if (map.getClass() != HashMap.class){
xStreamConfig.getAliasMap().put(rootElementName, map.getClass());
}
return toXML(map, xStreamConfig);
}
/**
* 将object转成xml字符串.
*
* 使用示例:
*
*
*
*
* 对于将下面的对象,转成XML
* User user = new User(1L);
*
*
*
* 1.不带XStreamConfig参数
* 使用 {@code com.feilong.xml.XmlUtil#toXML(user, null)},则返回
*
*
* {@code
*
* feilong
* 1
*
*
*
* }
*
*
*
*
*
*
*
* 2.设置 Alias 参数
* 可以看到上面的结果中,XML Root元素名字是 com.feilong.test.User,如果只是显示成 {@code }怎么做呢?
* 使用
*
*
*
* User user = new User(1L);
* XStreamConfig xStreamConfig = new XStreamConfig();
* xStreamConfig.getAliasMap().put("user", User.class);
* LOGGER.info(XmlUtil.toXML(user, xStreamConfig));
*
*
*
* ,则返回
*
*
* {@code
*
* feilong
* 1
*
*
*
* }
*
*
*
*
*
* 3.设置 ImplicitCollection 参数
* 如果我在结果不想出现 {@code }怎么做呢?
* 使用
*
*
*
* User user = new User(1L);
* XStreamConfig xStreamConfig = new XStreamConfig();
* xStreamConfig.getAliasMap().put("user", User.class);
* xStreamConfig.getImplicitCollectionMap().put("userAddresseList", User.class);
* LOGGER.info(XmlUtil.toXML(user, xStreamConfig));
*
*
*
* ,则返回
*
*
* {@code
*
* feilong
* 1
*
*
* }
*
*
*
*
*
* @param bean
* the obj
* @param xStreamConfig
* the to xml config
* @return 如果 bean
是null,抛出 {@link NullPointerException}
* @see com.feilong.lib.xstream.XStream#toXML(Object)
* @see com.feilong.lib.xstream.XStream#alias(String, Class)
* @see com.feilong.lib.xstream.XStream#addImplicitCollection(Class, String)
*/
public static String toXML(Object bean,XStreamConfig xStreamConfig){
Validate.notNull(bean, "bean can't be null!");
if (LOGGER.isDebugEnabled()){
String pattern = "class:[{}],bean:[{}],xStreamConfig:[{}]";
LOGGER.debug(pattern, bean.getClass().getSimpleName(), JsonUtil.format(bean), JsonUtil.format(xStreamConfig));
}
return XStreamUtil.toXML(bean, xStreamConfig);
}
//---------------------------------------------------------------
/**
* 将{@code xml}字符串转成 Map.
*
* 注意:
*
*
* - 内部封装了 {@link XStream#ignoreUnknownElements()} 忽略了不存在的字段
*
*
* 示例:
*
*
*
*
* String xml = "{@code \r\n" + " \r\n"
* + " \r\n" + " \r\n"
* + " \r\n" + " \r\n"
* + " \r\n" + " \r\n"
* + " }";
*
* Map{@code } map = XmlUtil.fromXML(xml, "xml");
* LOGGER.debug(JsonUtil.format(map));
*
*
* 返回:
*
*
{
"result_code": "SUCCESS",
"sign": "288AE0E455273102147B9CF95F43D222",
"mch_id": "1239453402",
"sub_mch_id": "",
"return_msg": "OK",
"appid": "wx2cc3b3d8bb8df520",
"nonce_str": "I1dy6p9hOy324Q2M",
"return_code": "SUCCESS"
}
*
*
*
*
* @param xml
* the xml
* @param rootElementName
* the root element name
* @return 如果 rootElementName
是null,抛出 {@link NullPointerException}
* 如果 rootElementName
是blank,抛出 {@link IllegalArgumentException}
* 如果 xml
是null,返回 null
* @since 3.0.0 change name from fromXMl
*/
public static Map toMap(String xml,String rootElementName){
Validate.notBlank(rootElementName, "rootElementName can't be blank!");
if (isNullOrEmpty(xml)){
return null;
}
return toBean(xml, XStreamConfigBuilder.buildSimpleMapXStreamConfig(rootElementName));
}
/**
* 将 xml
字符串,转成带注解的 processAnnotationsType
对象.
*
* 注意:
*
*
* - 内部封装了 {@link XStream#ignoreUnknownElements()} 忽略了不存在的字段
*
*
*
* 示例:
*
*
*
* 已知从微信得到下列xml
*
*
* {@code
}
*
*
* 需要转换成下列的 WechatCloseResponse 对象
*
*
{@code @XStreamAlias("xml")}
public class WechatCloseResponse{
//** 返回状态码 return_code 是 String(16) SUCCESS SUCCESS/FAIL.
{@code @XStreamAlias("return_code")}
private String return_code;
//** 返回信息 return_msg 否 String(128).
{@code @XStreamAlias("return_msg")}
private String return_msg;
//** 公众账号ID appid 是 String(32) wx8888888888888888 微信分配的公众账号ID.
{@code @XStreamAlias("appid")}
private String appid;
//** 商户号 mch_id 是 String(32) 1900000109 微信支付分配的商户号.
{@code @XStreamAlias("mch_id")}
private String mch_id;
//** 随机字符串 nonce_str 是 String(32) 5K8264ILTKCH16CQ2502SI8ZNMTM67VS 随机字符串,不长于32位。推荐随机数生成算法.
{@code @XStreamAlias("nonce_str")}
private String nonce_str;
//** 签名 sign 是 String(32) C380BEC2BFD727A4B6845133519F3AD6 签名,验证签名算.
//@XStreamAsAttribute
{@code @XStreamAlias("sign")}
private String sign;
//** 业务结果 result_code 是 String(16) SUCCESS SUCCESS/FAIL.
{@code @XStreamAlias("result_code")}
private String result_code;
//** 业务结果描述 result_msg 是 String(32) OK 对于业务执行的详细描述.
{@code @XStreamAlias("result_msg")}
private String result_msg;
//** 错误代码 err_code 否 String(32) SYSTEMERROR 详细参见第6节错误列表.
{@code @XStreamAlias("err_code")}
private String err_code;
//** 错误代码描述 err_code_des 否 String(128) 系统错误 结果信息描述.
{@code @XStreamAlias("err_code_des")}
private String err_code_des;
// setter/getter 省略
}
*
*
* 你可以
*
*
* String xml = {@code " \r\n" + " \r\n"
* + " \r\n" + " \r\n"
* + " \r\n" + " \r\n"
* + " \r\n"
* + " \r\n" + " ";}
*
* WechatCloseResponse map = XmlUtil.fromXML(xml, WechatCloseResponse.class);
* LOGGER.debug("{}", JsonUtil.format(map));
*
*
* 返回:
*
*
* {
* "sign": "288AE0E455273102147B9CF95F43D222",
* "result_code": "SUCCESS",
* "mch_id": "1239453402",
* "err_code": "",
* "result_msg": "",
* "return_msg": "OK",
* "err_code_des": "",
* "appid": "wx2cc3b3d8bb8df520",
* "return_code": "SUCCESS",
* "nonce_str": "I1dy6p9hOy324Q2M"
* }
*
*
*
*
* @param
* the generic type
* @param xml
* the xml
* @param processAnnotationsType
* the process annotations type
* @return 如果 xml
是null,返回 null
* 如果 processAnnotationsType
是null,抛出 {@link NullPointerException}
* @since 3.0.0 change name from fromXMl
*/
public static T toBean(String xml,Class processAnnotationsType){
Validate.notNull(processAnnotationsType, "processAnnotationsType can't be blank!");
if (isNullOrEmpty(xml)){
return null;
}
return toBean(xml, new XStreamConfig(processAnnotationsType));
}
/**
* From xml.
*
* 注意:
*
*
* - 内部封装了 {@link XStream#ignoreUnknownElements()} 忽略了不存在的字段
*
*
*
* @param
* the generic type
* @param xml
* the xml
* @param xStreamConfig
* the x stream config
* @return 如果 xml
是null或者是empty,返回 null
* @since 3.0.0 change name from fromXMl
*/
public static T toBean(String xml,XStreamConfig xStreamConfig){
if (LOGGER.isDebugEnabled()){
LOGGER.debug("input params info,xml:[{}],xStreamConfig:[{}]", xml, JsonUtil.format(xStreamConfig));
}
//---------------------------------------------------------------
if (isNullOrEmpty(xml)){
return null;
}
return XStreamUtil.toBean(xml, xStreamConfig);
}
}