【问题标题】:Writing output XML based on adjustments to input XML根据输入 XML 的调整编写输出 XML
【发布时间】:2021-12-24 01:11:10
【问题描述】:

我正在尝试重构现有工具以减少内存使用量。该工具会处理一个 XML 文件,该文件的开头如下:

<?xml version="1.0" encoding="utf-8"?>
<XQ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="my.xsd" SchemaVersion="2.0" SoftwareVersion="2.10.6.195" ExportMode="StrongReferences" System="foo" Database="bar" Description="descriptio of bar database on foo system" Created="2021-11-10T15:14:57.8590869Z" id="9632241b-2b2b-46a4-81b0-fb9bd65c2ef5" ParentKey="a743efc8-7095-4791-b44c-da70bb01f075" ExportedObject="wibble" ExportedType="baz" Identity="bif" Persist="142 {9895150E-085D-4fcb-A16D-5EF5D2527196} 2\{a743efc8-7095-4791-b44c-da70bb01f075}\{9632241b-2b2b-46a4-81b0-fb9bd65c2ef5}*foo\bar">
  <APDatabase>
    <id>11111111-2222-3333-4444-555555555555</id>
    <Name>foo</Name>
    <Description>foo database</Description>
    <APAttCat>
      <id>22222222-2222-2222-2222-222222222222</id>
      <Name>just a name</Name>
    </APAttCat>
    <APElemTemp>
      <id>6012ede0-c202-4474-a13a-d9cc349c638e</id>
      <Name>name of this elem temp</Name>
      <Description>description of this elem temp</Description>
      <BaseTemplateOnly>false</BaseTemplateOnly>
      <Type>None</Type>
      <InstanceType>Elem</InstanceType>
      <AllowElemToExtend>true</AllowElemToExtend>
      <APAttTemp>
        <id>33333333-3333-3333-3333-333333333333</id>
        <Name>Name of this att temp</Name>
        <Description>Description of this att temp</Description>
        <Type>String</Type>
        <Value type="String"></Value>
        <AttCatRef id="44444444-4444-4444-4444-444444444444">!Configuration</AttCatRef>
      </APAttTemp>
    </APElemTemp>
...

这些文件中还有很多内容,最终可能会变得庞大。重要的方面是每个&lt;AP...&gt; XML 元素的第一个子元素是一个&lt;id&gt; 元素,其中包含该父元素的guid。当前程序将整个内容加载到 XDocument 中,并向所有 '' 元素添加一个 'delete="true"' 属性,其中它们的子 &lt;id&gt; 元素不存在于单独的 guid 列表中,然后保存到另一个文件。

例如,如果 APAttTemp (33333333-3333-3333-3333-333333333333) 的 guid 不在我单独的 guid 列表中,我需要写 &lt;APAttTemp delete="true"&gt;

但是将整个内容加载到内存中会导致问题。我想做同样的事情,但不将整个 xml 加载到内存中。我可以用 XmlReader/XmlWriter 做到这一点吗?有没有更好的办法?

我是 XML 处理的新手,但到目前为止,我有一个阅读器和编写器来打开源 XML 并复制它。

正如@dbc 所说,需要提前查看子&lt;id&gt; 元素以发现当前元素是否需要修改。我在想也许我可以缓存任何&lt;AP...&gt; 元素并读取下一个&lt;id&gt; 元素,然后再将它们都写入输出?

【问题讨论】:

  • 能否请您edit 澄清您要从显示的输入 XML 生成的 XML?例如。您想将&lt;Name&gt; 转换为&lt;Name delete="true"&gt;,因为&lt;Name&gt; 没有&lt;id&gt; 子元素,但您不想将它添加到&lt;ElemTemp&gt;,因为它有?那么&lt;AttCatRef id="44444444-4444-4444-4444-444444444444"&gt; 呢?显然你不能删除根元素,但它会有&lt;id&gt; 子元素吗?
  • 在没有 minimal reproducible example 的情况下,我的猜测是你需要通过文件两次,因为 1) XmlReaderXmlWriter 是只转发的,但是 2) 你希望在扫描其子元素以查找 &lt;id&gt; 元素后向元素添加属性,这意味着您需要从当前阅读器位置向后写。
  • &lt;id&gt; 元素是否总是 first 子元素?
  • 嗨@dbc,是的,&lt;id&gt; 始终是第一个元素

标签: xml linq-to-xml xmlreader xmlwriter


【解决方案1】:

如您所知,第一个元素是 id 元素,如果值不在列表中,您需要为父元素输出一个属性,您应该能够以流式方式执行此操作,例如使用带流式传输的 XSLT 3(支持带有 Saxon EE 的 .NET 框架和带有 SaxonCS 的 .NET Core):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all"
  expand-text="yes">
  
  <xsl:param name="ids-to-keep" as="xs:string*" select="'11111111-2222-3333-4444-555555555555', '22222222-2222-2222-2222-222222222222', '6012ede0-c202-4474-a13a-d9cc349c638e'"/>
  
  <xsl:template match="*[starts-with(local-name(), 'AP')]/id">
    <xsl:apply-templates/>
  </xsl:template>
  
  <xsl:template match="*[starts-with(local-name(), 'AP')]/id/text()">
    <xsl:if test="not(. = $ids-to-keep)">
      <xsl:attribute name="delete">yes</xsl:attribute>
    </xsl:if>
    <id>{.}</id>
  </xsl:template>

  <xsl:strip-space elements="*"/>
  <xsl:output method="xml" indent="yes"/>

  <xsl:mode on-no-match="shallow-copy" streamable="yes"/>

</xsl:stylesheet>

也可以结合 XmlReader 和 XmlWriter 来复制所有内容,但如果遇到不在 id 列表中的 id,则写出该属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-05
    • 1970-01-01
    • 2012-01-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多