结构化数据格式转换为 XML

结构化数据格式转换为 XML

我有一些结构化数据格式的输出,需要将其转换为 XML。

我的用例是帕洛阿尔托防火墙通过命令行界面提供配置输出,该输出不能用于恢复配置。需要将其转换为 XML 才能使用。

格式是(只是大文件的一个片段来了解想法):

unknown-applications {
  unknown-tcp {
    destinations-per-hour 10;
    sessions-per-hour 10;
    session-length {
      maximum-bytes 100;
      minimum-bytes 50;
    }
  }
  unknown-udp {
    destinations-per-hour 10;
    sessions-per-hour 10;
    session-length {
      maximum-bytes 100;
      minimum-bytes 50;
    }
  }
}

并且需要

<unknown-applications>
  <unknown-tcp>
    <destinations-per-hour>10</destinations-per-hour>
    <sessions-per-hour>10</sessions-per-hour>
    <session-length>
      <maximum-bytes>100</maximum-bytes>
      <minimum-bytes>50</minimum-bytes>
    </session-length>
  </unknown-tcp>
  <unknown-udp>
    <destinations-per-hour>10</destinations-per-hour>
    <sessions-per-hour>10</sessions-per-hour>
    <session-length>
      <maximum-bytes>100</maximum-bytes>
      <minimum-bytes>50</minimum-bytes>
    </session-length>
  </unknown-udp>
</unknown-applications>

有没有类似解析器的东西可以实现这一点?

编辑:因为评论不允许我发布这么多:谢谢,看起来很有希望!

有些部分没有正确转换,因为我没有列出它的示例:例如

          import {
            network {
              interface [ ethernet1/5 ethernet1/6];
            }
          }

被转换为

<import>
<network>
<interface>[</interface>
</network>
</import>

但应该是

         <import>
            <network>
              <interface>
                <member>ethernet1/5</member>
                <member>ethernet1/6</member>
              </interface>
            </network>
          </import>

我看到有一些具体的事情,例如 [] 使其成为成员,所以我不确定手动执行此操作是否可行...原始格式也应该是 xml 格式

答案1

由于您的输入格式是非标准的,因此您需要编写一个解析器来处理它。有两种方法:您可以用任何过程编程语言“手动”编写它(通常作为自上而下的递归下降解析器),或者您可以使用某种解析器生成器。在后一种方法中,您定义输入语法的 BNF,该工具会为您构建解析器。如果您会使用 Scala,那么这一切就会变得非常容易。

XML 世界中经常使用的另一个解析器生成器是 REx - 它很方便,因为它将在 XQuery 或 XSLT 中生成解析器,这使得问题的 XML 生成方面非常容易;这是一个很棒的软件,但遗憾的是它的文档很少。另一个面向 XML 的工具是 Stephen Pemberton 的“Invisible XML”(https://homepages.cwi.nl/~steven/ixml/)——如果 REx 是很棒的软件,但文档很差,那么遗憾的是,Invisible XML 是很好的文档,但已发布的软件不多。

我想到了另一种廉价而愉快的方法:使用基于正则表达式的相当简单的编辑器脚本,您可以:

  • 将名称放在引号中并添加终止冒号
  • 将分号替换为逗号

然后你就会得到许多 JSON 解析器会接受的东西(这不太符合要求,因为你的分号是终止符而不是分隔符,但许多 JSON 解析器可以容忍这一点。)

答案2

以下是一个简单的awk程序,假设数据看起来与您所显示的完全一样,即

  1. 开头的“标签”位于仅包含标签和{,的行上
  2. 带有数据的行恰好包含一个标签,后跟一个单词和 a ;,并且
  3. 每个都}出现在没有其他内容的行上。

它使用堆栈来跟踪当前正在使用的标签,并在新标签出现在输入中时将其推送到堆栈中。当找到 a 时},输出栈顶的结束标记。数据行的检测是通过它们不以单独字符{或字符结尾的事实来进行的}

没有尝试以任何方式验证数据。

$NF == "{" {
        stack[++top] = $1                       # push tag to stack
        printf "<%s>\n", stack[top]             # output opening tag
        next
}

$NF == "}" {
        printf "</%s>\n", stack[top--]          # output closing tag + pop stack
        next
}

{
        sub(";$", "", $2)                       # remove ; from EOL
        printf "<%s>%s</%s>\n", $1, $2, $1      # output tag with data
}

对于您的示例数据,awk -f script.awk data.in产生

<unknown-applications>
<unknown-tcp>
<destinations-per-hour>10</destinations-per-hour>
<sessions-per-hour>10</sessions-per-hour>
<session-length>
<maximum-bytes>100</maximum-bytes>
<minimum-bytes>50</minimum-bytes>
</session-length>
</unknown-tcp>
<unknown-udp>
<destinations-per-hour>10</destinations-per-hour>
<sessions-per-hour>10</sessions-per-hour>
<session-length>
<maximum-bytes>100</maximum-bytes>
<minimum-bytes>50</minimum-bytes>
</session-length>
</unknown-udp>
</unknown-applications>

当运行时xmlstarlet fo,它会变成

<?xml version="1.0"?>
<unknown-applications>
  <unknown-tcp>
    <destinations-per-hour>10</destinations-per-hour>
    <sessions-per-hour>10</sessions-per-hour>
    <session-length>
      <maximum-bytes>100</maximum-bytes>
      <minimum-bytes>50</minimum-bytes>
    </session-length>
  </unknown-tcp>
  <unknown-udp>
    <destinations-per-hour>10</destinations-per-hour>
    <sessions-per-hour>10</sessions-per-hour>
    <session-length>
      <maximum-bytes>100</maximum-bytes>
      <minimum-bytes>50</minimum-bytes>
    </session-length>
  </unknown-udp>
</unknown-applications>

相关内容