如何在XML文档上应用一组结构化的,通用的,嵌套的过滤器?

How to apply a set of structured, general, nested filters on XML document?

提问人:kraytdragon 提问时间:11/10/2023 更新时间:11/10/2023 访问量:49

问:

我有一组 XML 文档,我需要根据父级的一组条件进行筛选,并根据匹配父级的后代进行筛选。我希望用户能够编写一组可以以这种方式应用的结构化过滤器,无论是使用嵌套字典还是使用 PLY 之类的东西解析的查询。我的XML文档可能如下所示:

<data>
  <encounter type="Example" start="2015-01-01 00:00:00">
    <instance start="2015-01-01 00:00:00">
      <sectionA type="Example A" start="2015-01-01 00:10:00">SOME TEXT</sectionA>
      <SectionB start="2015-01-01 00:20:00">
        <paragraph>SOME TEXT</paragraph>
      </SectionB>
    </instance>
    <instance start="2015-01-02 00:00:00">
      <SectionC start="2015-01-02 00:10:00">
        <paragraph>SOME TEXT</paragraph>
      </SectionC>
    </instance>
  </encounter>
  <encounter type="Example" start="2015-03-01 00:00:00">
    <instance start="2015-03-01 00:00:00">
      <sectionA type="Example A" start="2015-03-01 00:10:00">SOME TEXT</sectionA>
      <sectionA type="Example A" start="2015-03-01 00:20:00">SOME TEXT</sectionA>
    </instance>
    <instance start="2015-03-02 00:00:00">
      <SectionC start="2015-03-02 00:10:00">
        <paragraph>SOME TEXT</paragraph>
      </SectionC>
    </instance>
  </encounter>
</data>

例如,如果我想要所有

  • “遭遇”,其中“开始”介于“01/01/2014”和“01/02/2015”之间,类型为“示例”。
  • 在这些“遭遇”中,仅返回 sectionA 和 sectionB 类型的后代标记。对于 sectionA 标签,请仅抓取类型为“ExampleA”的标签

它将返回如下内容:

<data>
  <encounter type="Example" start="2015-01-01 00:00:00">
    <instance start="2015-01-01 00:00:00">
      <sectionA type="Example A" start="2015-01-01 00:10:00">SOME TEXT</sectionA>
      <SectionB start="2015-01-01 00:20:00">
        <paragraph>SOME TEXT</paragraph>
      </SectionB>
    </instance>
  </encounter>
</data>

实现这一目标的最佳方法是什么?目前,我有一组嵌套词典,例如:

{
    'PARENT': 'TAG1',
    'TAG1': {
        'ATTRIBUTE1': {'OPERATOR': 'VALUE'},
        'ATTRIBUTE2': {'OPERATOR': 'VALUE'}, 
    },
    'TAG2': {
        'ATTRIBUTE1': {'OPERATOR': 'VALUE'},
    }
}

然后我遍历 XML 树以查找匹配的父项,然后匹配子项,但我想知道是否有一种方法可以使用 xpaths 或其他一些工具进行优化。

python xml lxml

评论

0赞 LMC 11/10/2023
请花一些时间阅读 [如何创建一个最小的、可重现的示例。此外,使用 xpath 不会按请求返回 xml 片段,除非它是显式构建的。

答:

0赞 Michael Kay 11/10/2023 #1

您似乎已经设计了一种特殊用途的转换语言,使用 JSON 语法来允许指定一类过滤操作。

我过去实现此类语言的方法是编写一个 XSLT 转换,将专用语言转换为 XSLT,然后执行 XSLT。由于您的语言使用 JSON 而不是 XML 语法,因此您可能需要 XSLT 3.0 来完成此作业。

这项工作最困难的部分可能是为自定义语言的语法和语义编写一个清晰明确的定义:然后编写一组好的测试用例。

评论

0赞 kraytdragon 11/11/2023
你有什么技巧可以学习为此目的编写 XLST 转换吗?我以前从未使用过它们
0赞 Michael Kay 11/11/2023
是的,我愿意。读我的书!
0赞 jdweng 11/10/2023 #2

我喜欢将 Powershell 与 Xml Linq 一起使用

using assembly System.Xml.Linq

$inputFilename = "c:\temp\test.xml"
$outputFilename = "c:\temp\test1.xml"

$start = '01/01/2014'
$end = '01/02/2015'
$type = 'Example'

$startDate = [DateTime]::Parse($start)
$endDate = [DateTime]::Parse($end)

$doc = [System.Xml.Linq.XDocument]::Load($inputFilename)

$encounters = $doc.Descendants("encounter")
$dates = [System.Linq.Enumerable]::Where($encounters,  [Func[object,bool]]{ param($x) 
   ([DateTime]::Parse($x[0].Attribute('start').Value) -ge $startDate) -and 
   ([DateTime]::Parse($x[0].Attribute('start').Value) -le $endDate) -and
   ($x[0].Attribute('type').Value -eq $type)})

$dates = [pscustomobject]@($dates)
$data = $doc.Descendants('data')[0]
$data.RemoveNodes()
$data.Add($dates)
$doc.Save($outputFilename)

结果

<?xml version="1.0" encoding="utf-8"?>
<data>
  <encounter type="Example" start="2015-01-01 00:00:00">
    <instance start="2015-01-01 00:00:00">
      <sectionA type="Example A" start="2015-01-01 00:10:00">SOME TEXT</sectionA>
      <SectionB start="2015-01-01 00:20:00">
        <paragraph>SOME TEXT</paragraph>
      </SectionB>
    </instance>
    <instance start="2015-01-02 00:00:00">
      <SectionC start="2015-01-02 00:10:00">
        <paragraph>SOME TEXT</paragraph>
      </SectionC>
    </instance>
  </encounter>
</data>