使用Java构造XML字符串

概述

项目需要构造如下的XML字符串(如果需要xml头部的声明,可自行去除代码的配置即可)

<xml>
  <ToUserName><![CDATA[ToUserName]]></ToUserName>
  <FromUserName><![CDATA[FromUserName]]></FromUserName>
  <CreateTime>CreateTime</CreateTime>
  <MsgType><![CDATA[MsgType]]></MsgType>
  <TemplateCard>
    <CardType><![CDATA[CardType]]></CardType>
    <MainTitle>
      <Title><![CDATA[MainTitle]]></Title>
    </MainTitle>
    <SubTitleText><![CDATA[SubTitleText]]></SubTitleText>
    <ButtonList>
      <Text><![CDATA[button_1]]></Text>
      <Style>1</Style>
      <Key><![CDATA[button_key_1]]></Key>
    </ButtonList>
    <ReplaceText><![CDATA[已提交]]></ReplaceText>
  </TemplateCard>
</xml>

实现方式

使用如下工具类可以自行构建想要的XML字符串。需要引入lombok依赖(懒得写get和set方法了)

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>
package com.jusdaglobal.aichat.util;

import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

@Slf4j
public class XmlBuilderUtils {
    
    /**
     * 构造XML字符串
     * @param rootElementName 根元素名
     * @param nodeList        节点列表
     * @return 构造的XML字符串
     */
    public static String build(String rootElementName, List<Node> nodeList) {

        // 创建DocumentBuilderFactory和DocumentBuilder对象
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder;
        try {
            dBuilder = dbFactory.newDocumentBuilder();
            // 创建新的Document对象和根元素
            Document doc = dBuilder.newDocument();
            Element rootElement = doc.createElement(rootElementName);

            doc.appendChild(rootElement);

            // 添加子节点和属性到根元素
            for (Node node : nodeList) {
                Element element = doc.createElement(node.getName());
                if (node.isNeedCdata) {
                    //判断结点是否需要 CDATA 片段
                    //CDATA 片段:在 XML 中,CDATA 可以直接包含未经转义的文本。比如 < 和 &,只要位于 CDATA 片段中,它们就不需要被转义,保持原样就可以了
                    element.appendChild(doc.createCDATASection(node.getValue()));
                } else if (StringUtils.hasText(node.getValue())){
                    element.setTextContent(node.getValue());
                }
                if (Objects.nonNull(node.getAttributeList())) {
                    //设置结点的属性值
                    for (Attribute attribute : node.getAttributeList()) {
                        element.setAttribute(attribute.getName(), attribute.getValue());
                    }
                } else if (Objects.nonNull(node.getChildrenList())) {
                    //如果该结点有子节点,则添加子节点的值
                    for (Node child : node.getChildrenList()) {
                        Element childElement = getChildrenNode(child,doc);

                        //判断是否还有子节点
                        if (!CollectionUtils.isEmpty(child.getChildrenList())) {
                            for (Node childrenNode : child.getChildrenList()) {
                                Element childrenElement = getChildrenNode(childrenNode,doc);
                                childElement.appendChild(childrenElement);
                            }
                        }

                        //添加子节点
                        element.appendChild(childElement);
                    }
                }

                //将结点添加到根结点下
                rootElement.appendChild(element);
            }

            Transformer transformer = TransformerFactory.newInstance().newTransformer();
            //去除XML头部声明(<?xml version="1.0" encoding="UTF-8" standalone="no"?>)
            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
            //启用缩进
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            //指定了缩进量,可以将 "2" 替换成其他值,如 "4" 或 "tab" 等
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            // 设置字符编码
            transformer.setOutputProperty(OutputKeys.ENCODING, StandardCharsets.UTF_8.name());

            // 将新的Document对象转换为字符串输出
            StringWriter writer = new StringWriter();
            transformer.transform(new DOMSource(doc), new StreamResult(writer));
            return writer.toString().trim();
        } catch (Exception e) {
            log.error("构造XML异常{}",e.getMessage());
        }

        return "";
    }

    /**
     * 构造子节点
     */
    private static Element getChildrenNode(Node child, Document doc){
        Element childElement = doc.createElement(child.getName());
        if (child.isNeedCdata) {
            childElement.appendChild(doc.createCDATASection(child.getValue()));
        } else if (StringUtils.hasText(child.getValue())){
            childElement.setTextContent(child.getValue());
        }
        //设置子结点的属性值
        if (Objects.nonNull(child.getAttributeList())) {
            for (Attribute attribute : child.getAttributeList()) {
                childElement.setAttribute(attribute.getName(), attribute.getValue());
            }
        }

        return childElement;
    }

    /**
     * 节点类
     */
    @Data
    @AllArgsConstructor
    public static class Node {
        /**结点名称*/
        private final String name;
        /**结点值*/
        private final String value;
        /**结点属性*/
        private final List<Attribute> attributeList;
        /**结点的子节点*/
        private final List<Node> childrenList;
        /**结点是否需要 CDATA 片段*/
        private boolean isNeedCdata;
    }

    /**
     * 属性类
     */
    @Data
    @AllArgsConstructor
    public static class Attribute {
        /**属性名称*/
        private final String name;
        /**属性值*/
        private final String value;
    }
}

测试代码

public static void main(String[] args) throws Exception {

    List<Node> nodeList = new ArrayList<>();

    nodeList.add(new Node("ToUserName", "ToUserName", null,null,true));
    nodeList.add(new Node("FromUserName", "FromUserName", null,null,true));
    nodeList.add(new Node("CreateTime", "CreateTime", null,null,false));
    nodeList.add(new Node("MsgType", "MsgType", null,null,true));

    List<Node> childNodes = new ArrayList<>();
    childNodes.add(new Node("CardType","CardType",null,null,true));
    childNodes.add(new Node("MainTitle",null,null,List.of(new Node("Title","MainTitle",null,null,true)),false));
    childNodes.add(new Node("SubTitleText","SubTitleText",null,null,true));

    List<Node> button_1 = new ArrayList<>();
    button_1.add(new Node("Text","button_1",null,null,true));
    button_1.add(new Node("Style","1",null,null,false));
    button_1.add(new Node("Key","button_key_1",null,null,true));
    childNodes.add(new Node("ButtonList",null,null,button_1,false));

    childNodes.add(new Node("ReplaceText","已提交",null,null,true));
    nodeList.add(new Node("TemplateCard", null, null,childNodes,false));
    System.out.println(build("xml", nodeList));
}
posted @ 2023-05-28 14:26  伊文小哥  阅读(319)  评论(0编辑  收藏  举报