我有一些结构化数据格式的输出,需要将其转换为 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
程序,假设数据看起来与您所显示的完全一样,即
- 开头的“标签”位于仅包含标签和
{
,的行上 - 带有数据的行恰好包含一个标签,后跟一个单词和 a
;
,并且 - 每个都
}
出现在没有其他内容的行上。
它使用堆栈来跟踪当前正在使用的标签,并在新标签出现在输入中时将其推送到堆栈中。当找到 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>