如何解析XML并以自定义方式再次序列化它,将空元素显示为扩展标签?

How to parse XML and serialize it again in a custom way which shows empty elements as expanded tags?

提问人:user2950720 提问时间:12/14/2022 最后编辑:Peter Seligeruser2950720 更新时间:12/21/2022 访问量:107

问:

我有以下可以是动态的

const data = '<users>
 <user>
  <firstname>test</firstname>
  <lastname />
  <age />
 </user>
</users>'

我想找到它用 a 和 expand 关闭的每个实例,例如变成 /><age /><age></age>

显然,我可以使用命名标签来做到这一点,例如

data = data.replaceAll("age />", "age></age>")

但是,对于我不知道名称的标签,我如何在全球范围内执行此操作?

JavaScript 解析序列 解析 XML 序列化

评论

5赞 evolutionxbox 12/14/2022
考虑不使用字符串替换来编辑 xml?
0赞 Kaddath 12/14/2022
当然,您可以使用正则表达式,但是您确定您的标签永远不会有属性,从而使此任务更加困难吗?
3赞 T.J. Crowder 12/14/2022
你为什么要这样做? 在XML中的意思完全相同。(不是 HTML 格式,而是 XML 格式。<age /><age></age>
0赞 T.J. Crowder 12/14/2022
@Kaddath - 如果您要定位的元素永远不会有属性,那么使用单个正则表达式(我认为)是可行的。但在一般情况下,如果可能的话,一个可靠且正确地处理以下元素的单个 JavaScript 正则表达式似乎会非常复杂。y<?xml version="1.0" encoding="UTF-8"?><x><y a="/>" b='/>' c="'" d='"' e=">"></y></x>
1赞 Kaddath 12/14/2022
@T.J.Crowder:是的,这就是我问的原因,如果这是一个没有属性的元素的简单模板转换,它可能是值得的,但由于你的问题更相关,我一直在等待答案

答:

0赞 Peter Seliger 12/15/2022 #1

从上面的评论中......

“OP 可以通过 DOMParser.parseFromString 创建一个 XMLDocument,例如 const doc = (new DOMParser).parseFromString(data, 'application/xml');但是,OP 需要编写自己的序列化功能,因为 XMLSerializer serializeToString 方法会将每个空节点序列化为其空标记表示形式。

下一个提供的示例代码附带一个自定义序列化程序,以证明建议的方法可以毫不费力地实现。

const data = `<users lang="en">
  <user uuid="1232-3965-6923-2887">
    <firstname>test</firstname>
    <lastname/>
    <age type="integer"/>
  </user>
</users>`;

const xmlDoc = (new DOMParser).parseFromString(data, 'application/xml');

const xmlSerializerMarkup = (new XMLSerializer).serializeToString(xmlDoc);
const documentElementMarkup = xmlDoc.documentElement.outerHTML;

console.log('originally provided markup...\n', data);

console.log('XMLSerializer markup...\n', xmlSerializerMarkup);
console.log('documentElement markup...\n', documentElementMarkup);

function serialize(elmNode, indention = '') {
  const { nodeName, childElementCount } = elmNode;

  let currentMarkup = elmNode
    .getAttributeNames()
    .reduce((markup, attrName) => [
        markup,
        ' ',
        attrName,
        '="',
        elmNode.getAttribute(attrName),
        '"',
    ].join(''), `${ indention }<${ nodeName }`);

  if (childElementCount === 0) {
    const { textContent } = elmNode;

    if (textContent === '') {
      // entirely empty element node.

      // do not implement/support the empty tag style.
      currentMarkup = currentMarkup + `><\/${ nodeName }>`;
    } else {
      currentMarkup = [
        currentMarkup,
        '>',
        textContent,
        `<\/${ nodeName }>`,
      ].join('');
    }
  } else {
    let nestedMarkup = '';

    [...elmNode.children]
      .forEach(childElement => {
        nestedMarkup = [
          nestedMarkup,
          serialize(childElement, (indention + '  ')),
        ].join('\n');
      });

    currentMarkup = [
      currentMarkup,
      nestedMarkup,
    ].join('>') + `\n${ indention }<\/${ nodeName }>`;
  }
  return currentMarkup;
}
const customSerializerMarkup = serialize(xmlDoc.documentElement);

console.log('custom serializer markup...\n', customSerializerMarkup);
.as-console-wrapper { min-height: 100%!important; top: 0; }

0赞 skreutzer 12/16/2022 #2

您是否介意使用/改编像 JsStAX 这样的东西(完全披露:我的一个小移植项目,用于在 JavaScript 中实现 Java 的 XML 流式处理 API)?

好处是,使用外部事件循环,您可以很好地控制要生成的输出,并且不会遇到XML格式的任何变化。

它是否太重,是否缺少必需/所需的功能(因为它不是 XML 功能完整的),反对许可证?但是,除了许多可能的改进或其他可能更好的解决方案之外,这项工作确实如此。