繁体   English   中英

使用C#XML文档类修改子节点值

[英]Modify Child Node Value With C# XML Document Class

我真是个菜鸟,很抱歉,但是我到处搜索了,但仍然受困。

我正在编写一个程序,该程序将在用户选择初始XML文档并将其重命名后修改2个XML文档。 一旦用户更改了第一个XML的文件名(我们将其称为XML#1),其他两个XML文档必须将新文件名插入其中的特定节点值(XML#2和#3)。

每个XML文档都具有名称完全相同的多个“资产”节点,我需要使用XML文档#1中存在的唯一UUID值来区分所需的节点。

对于XML#2和#3,“ Id”节点都包含相同的唯一UUID。 我正在解析XML#1以获取此UUID,并将其分配给名为“ cpluuid”的变量。

然后,我在XML#2和#3中搜索“ Id” =到“ cpluuid”的节点,并尝试修改包含要插入文件名的正确节点。

XML文档#2-又名var =打包(省略了其他“资产”节点)

<Asset>
  <Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
  <AnnotationText>blah</AnnotationText>
  <Hash>5Yf4BV4GZ4qE9EjvtohZ8Rq8M2w=</Hash>
  <Size>21881</Size>
  <Type>text/xml</Type>
  <OriginalFileName>CPL_IMF_JOT_Sample_143.xml</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
</Asset>

我已经成功地以这种方式更新了XML Doc#2。 通过以下代码,“ OriginalFileName”节点已成功更新为变量值“ cpluuid”:

XmlDocument xmlDoc = new XmlDocument();
XmlNamespaceManager ns = new XmlNamespaceManager(xmlDoc.NameTable);
ns.AddNamespace("cplns1", "http://www.smpte-ra.org/schemas/2067-3/2016");
ns.AddNamespace("cplns2", "http://www.w3.org/2001/XMLSchema-instance");
ns.AddNamespace("pklns", "http://www.smpte-ra.org/schemas/2067-2/2016/PKL");
ns.AddNamespace("assetns", "http://www.smpte-ra.org/schemas/429-9/2007/AM");

xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(output);

string cpluuid = xmlDoc.SelectSingleNode("//cplns1:CompositionPlaylist/cplns1:Id", ns).InnerText;

xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(packing);

XmlNodeList nodeList;
XmlNode root = xmlDoc.DocumentElement;

nodeList = root.SelectNodes("descendant::pklns:Asset[pklns:Id=\"" + cpluuid + "\"]", ns);

foreach (XmlNode Asset in nodeList)
{
    Asset["OriginalFileName"].InnerText = (outfile);
}
xmlDoc.PreserveWhitespace = true;
xmlDoc.Save(packing);

但是,我在使用XML文档#3时遇到了麻烦,因为我需要修改的节点(“路径”)嵌套在树的更下方:

XML文档#3-又名var = Assetmap

<Asset>
  <Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
  <ChunkList>
    <Chunk>
      <Path>CPL_IMF_JOT_Sample.xml</Path>
      <VolumeIndex>1</VolumeIndex>
      <Offset>0</Offset>
      <Length>21881</Length>
    </Chunk>
  </ChunkList>
</Asset>

因此,我尝试使用类似的代码,但由于“ Path”不是“ Asset”的同级兄弟,因此无法弄清楚如何进一步深入研究。 此代码不会修改“路径”值。 我尝试使用不同的XPath语法,但我所知无用:

xmlDoc.PreserveWhitespace = true;
xmlDoc.Load(assetmap);

XmlNodeList nodeList2;

nodeList2 = root.SelectNodes("descendant::assetns:Asset[assetns:Id=\"" + cpluuid + "\"]", ns);

foreach (XmlNode Asset in nodeList2)
{
Asset["Path"].InnerText = (outfile);
}
xmlDoc.PreserveWhitespace = true;
xmlDoc.Save(assetmap);

我也尝试过使用System.Xml.Linq方法进行各种操作,但无法使其正常工作。

更新#1

这就是我一直尝试使用XDocument的方式。 似乎应该可以,但是总是返回对象引用错误。

var xml = XDocument.Load(assetmap);

var node = xml.Descendants("Asset").FirstOrDefault(asset => asset.Element("Id").Value == (cpluuid));

node.SetElementValue("Path", (outfile));

更新#2

我第二次尝试使用XDocument是基于@Juan M.Vergara的出色反馈。 不幸的是,虽然代码没有错误地执行,但XML节点不会更新为新值。

XDocument document = XDocument.Load(assetmap);

var assetElements = document.Elements("Asset");

foreach (var asset in assetElements)
{
    var innerElements = asset.Elements("Id");
    var matchingId = innerElements.FirstOrDefault(x => x.Value.Equals(cpluuid));
    if (matchingId == null)
    {
       MessageBox.Show("UUID not found");
       return;
    }

    var chunks = asset.Elements("ChunkList").First().Elements();

    foreach (var chunk in chunks)
    {
       chunk.Elements("Path").First().SetValue(outfile);
    }
}

document.Save(assetmap);

如果名称空间或其他内容有问题,这也是XML的较大部分。 需要更新的“路径”节点是树中的第二个节点:

<?xml version="1.0" encoding="utf-8"?>
<AssetMap xmlns="http://www.smpte-ra.org/schemas/429-9/2007/AM">
  <Id>urn:uuid:dc59ba55-adfd-4395-bdaa-de54202d014d</Id>
  <Creator>Colorfront Transkoder 2017</Creator>
  <VolumeCount>1</VolumeCount>
  <IssueDate>2018-02-16T20:59:42-00:00</IssueDate>
  <Issuer>Generic</Issuer>
  <AssetList>
    <Asset>
    <Id>urn:uuid:296a656c-3610-4de1-9b08-2aa63245788d</Id>
    <PackingList>true</PackingList>
    <ChunkList>
      <Chunk>
        <Path>PKL_UUID.xml</Path>
        <VolumeIndex>1</VolumeIndex>
        <Offset>0</Offset>
        <Length>3015</Length>
      </Chunk>
    </ChunkList>
    </Asset>
    <Asset>
    <Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
    <ChunkList>
      <Chunk>
        <Path>CPL_IMF_JOT_Sample.xml</Path>
        <VolumeIndex>1</VolumeIndex>
        <Offset>0</Offset>
        <Length>21881</Length>
      </Chunk>
   </ChunkList>
   </Asset>
 </AssetList>
</AssetMap>

更新#3

我现在正在为XML#2尝试相同的方法,但是没有用。 我认为是因为“ OriginalFileName”不是“ Asset”下的“ First”元素。 我正在尝试使用不同的语法组合,但是似乎没有任何效果(顺序不包含任何元素错误)

XDocument pkldoc = XDocument.Load(packing);

var pklns = pkldoc.Root.GetDefaultNamespace();
var packingList = pkldoc.Elements(pklns + "PackingList").First(); // this ignores the namespace
var pklassetList = packingList.Elements(pklns + "AssetList").First();
var pklassetElements = pklassetList.Elements(pklns + "Asset");

foreach (var pklasset in pklassetElements)
{
    var innerElements = pklasset.Elements(pklns + "Id");
    var matchingId = innerElements.FirstOrDefault(x => x.Value.Equals(cpluuid));
    if (matchingId == null)
    {
       //MessageBox.Show("UUID not found");
       continue;
    }

    var ofns = pklasset.Elements(pklns + "Asset").First().Elements();

    foreach (var ofn in ofns)
    {
       ofn.Elements(pklns + "OriginalFileName").First().SetValue(outfile);
     }
    }

更新#4

这是XML#2的全部内容。 结构与XML#3不同。 我只想更新最后一个“资产”树中的“ OriginalFileName”元素。

<?xml version="1.0" encoding="UTF-8"?>
<PackingList xmlns="http://www.smpte-ra.org/schemas/2067-2/2016/PKL">
<Id>urn:uuid:296a656c-3610-4de1-9b08-2aa63245788d</Id>
<AnnotationText>IMF_JOT_Sample</AnnotationText>
<IssueDate>2018-02-16T20:59:42-00:00</IssueDate>
<Issuer>Generic</Issuer>
<Creator>Generic</Creator>
<AssetList>
<Asset>
  <Id>urn:uuid:744f36b7-fc7e-4179-8b75-c71c18f98156</Id>
  <AnnotationText>Video_744f36b7-fc7e-4179-8b75-c71c18f98156.mxf</AnnotationText>
  <Hash>8HhnKnLn+Lp/Ik9i94Ml4SXAxH4=</Hash>
  <Size>14568486</Size>
  <Type>application/mxf</Type>
  <OriginalFileName>Video_744f36b7-fc7e-4179-8b75-c71c18f98156.mxf</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
  <Id>urn:uuid:bf5438ea-ba58-4ae0-a64a-5d23cee2ebb3</Id>
  <AnnotationText>Audio_bf5438ea-ba58-4ae0-a64a-5d23cee2ebb3.mxf</AnnotationText>
  <Hash>Wg4aEAE5Ji9e14ZyGkvfUUjBwCw=</Hash>
  <Size>4341294</Size>
  <Type>application/mxf</Type>
  <OriginalFileName>Audio_bf5438ea-ba58-4ae0-a64a-5d23cee2ebb3.mxf</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
  <Id>urn:uuid:dd5a88d2-ccec-4b22-8584-fda51945c3ea</Id>
  <AnnotationText>Audio_dd5a88d2-ccec-4b22-8584-fda51945c3ea.mxf</AnnotationText>
  <Hash>OwjRFnWZCdKHSZ+3PBXroDhMMlY=</Hash>
  <Size>1458414</Size>
  <Type>application/mxf</Type>
  <OriginalFileName>Audio_dd5a88d2-ccec-4b22-8584-fda51945c3ea.mxf</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
  <Id>urn:uuid:9e11458a-71fb-4702-8609-55d2308dcc64</Id>
  <AnnotationText>Sub_9e11458a-71fb-4702-8609-55d2308dcc64.mxf</AnnotationText>
  <Hash>48KyxgwCJVXIdgAGfaNApheQN5M=</Hash>
  <Size>34509</Size>
  <Type>application/mxf</Type>
  <OriginalFileName>Sub_9e11458a-71fb-4702-8609-55d2308dcc64.mxf</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
  <Id>urn:uuid:3f57f474-4c81-438e-a67d-1b08fa09a10d</Id>
  <AnnotationText>IMF_JOT_Sample</AnnotationText>
  <Hash>q8TiPkg/3devlN3LXnBhrgkZ968=</Hash>
  <Size>713</Size>
  <Type>text/xml</Type>
  <OriginalFileName>OPL_3f57f474-4c81-438e-a67d-1b08fa09a10d.xml</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>
<Asset>
  <Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
  <AnnotationText>CPL_IMF_JOT_Sample.xml</AnnotationText>
  <Hash>5Yf4BV4GZ4qE9EjvtohZ8Rq8M2w=</Hash>
  <Size>21881</Size>
  <Type>text/xml</Type>
  <OriginalFileName>CPL_IMF_JOT_Sample.xml</OriginalFileName>
  <HashAlgorithm Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
</Asset>

更新#5

再次感谢Juan的帮助。 这是XML Doc#2的工作解决方案。 我知道它不是很漂亮,但是可以。

在某个时候,我想返回并完全消化您的最后一个示例,并按照您的方式组合/清理我的代码。 谢谢!

XDocument pkldoc = XDocument.Load(packing);

var pklns = pkldoc.Root.GetDefaultNamespace();

var pklassetElements = pkldoc.Descendants(pklns + "Asset");

foreach (var pklasset in pklassetElements)
{
    var idElement = pklasset.Descendants(pklns + "Id").First();
    if (!idElement.Value.Equals(cpluuid))
    continue;


    SetNewValue(pklasset, pklns + "OriginalFileName", outfile);

}
void SetNewValue(XElement currentElement, XName elementName, string newValue)
                {
    var matchingElements = currentElement.Descendants(elementName);
    if (matchingElements.Any())
    {
        foreach (var element in matchingElements)
          element.SetValue(newValue);
                    }
                }
                pkldoc.Save(packing);

我只是尝试了一下,然后在控制台应用程序示例中整理了一些代码,请记住,在某些情况下我不控制所有错误(例如找不到元素...),但是我认为它可以给您一个思路如何使用System.Xml.Linq。

希望能帮助到你。

using System;
using System.Linq;
using System.Xml.Linq;

namespace ParsingXML
{

    class Program
    {
        const string _xml =
            @"<Asset>
              <Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
              <ChunkList>
                <Chunk>
                  <Path>CPL_IMF_JOT_Sample.xml</Path>
                  <VolumeIndex>1</VolumeIndex>
                  <Offset>0</Offset>
                  <Length>21881</Length>
                </Chunk>
              </ChunkList>
            </Asset>";

        static void Main(string[] args)
        {
            ReplacePath(
                @"urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c",
                @"c:\path\to\somefilename.xml"
                );

            Console.WriteLine("Enter to exit...");
            Console.ReadLine();
        }

        static void ReplacePath(string id, string pathToSet)
        {
            XDocument document = XDocument.Parse(_xml);

            var assetElements = document.Elements("Asset");

            foreach (var asset in assetElements)
            {
                var innerElements = asset.Elements("Id");
                var matchingId = innerElements.FirstOrDefault(e => e.Value.Equals(id));
                if (matchingId == null)
                {
                    Console.WriteLine("id not found");
                    return;
                }

                var chunks = asset.Elements("ChunkList").First().Elements();

                foreach (var chunk in chunks)
                {
                    chunk.Elements("Path").First().SetValue(pathToSet);
                }
            }
            var xml = document.ToString();
            Console.WriteLine(xml);

            Console.WriteLine("Enter to exit...");
            Console.ReadLine();
        }
    }
}

这是我得到的输出:

<Asset>
  <Id>urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c</Id>
  <ChunkList>
    <Chunk>
      <Path>c:\path\to\somefilename.xml</Path>
      <VolumeIndex>1</VolumeIndex>
      <Offset>0</Offset>
      <Length>21881</Length>
    </Chunk>
  </ChunkList>
</Asset>
Enter to exit...

更新资料

问题是资产不止一个,并且由于第一个资产不匹配,因此第一个foreach会给出一条消息并在返回中停止执行,只需用continue指令替换return语句,这样执行就不会停止。

另一方面,您可能必须包括tha名称空间,这是我最初的ReplacePath函数的更新代码:

    static void ReplacePath(string targetFile, string id, string outfile)
    {
        //XDocument document = XDocument.Parse(_xml);
        XDocument document = XDocument.Load(targetFile);
        // get root namespace to use with rest of element names
        var ns = document.Root.GetDefaultNamespace();

        var assetMap = document.Elements(ns + "AssetMap").First(); // this ignores the namespace
        var assetList = assetMap.Elements(ns + "AssetList").First();
        var assetElements = assetList.Elements(ns + "Asset");

        foreach (var asset in assetElements)
        {
            var innerElements = asset.Elements(ns + "Id");
            var matchingId = innerElements.FirstOrDefault(e => e.Value.Equals(id));
            if (matchingId == null)
            {
                continue;
            }

            var chunks = asset.Elements(ns + "ChunkList").First().Elements();

            foreach (var chunk in chunks)
            {
                var chunkElement = chunk.Elements(ns + "Path").First();
                chunkElement.SetValue(outfile);
            }
        }
        var xml = document.ToString();
        Console.WriteLine(xml);

        Console.WriteLine("writing to file");
        document.Save(targetFile);

        Console.WriteLine("Enter to exit...");
        Console.ReadLine();
    }

希望这次您得到正确的答案:)。

由于新的条件,我更愿意写一个不同的答案。 假设您有这2个示例xml,它们具有不同的结构但具有共同的节点,我又尝试了使用Descendants。

您基本上需要找到Asset元素,然后检查ID是否匹配,如果匹配,则更新Path或OriginalFileName元素,请尝试以下代码:

    private void Run()
    {
        XDocument doc1 = XDocument.Load("xml1.xml");
        XDocument doc2 = XDocument.Load("xml2.xml");
        var id = @"urn:uuid:d0686356-19c7-4bf4-b915-db778c308d1c";

        ReplaceId(doc1, id, "new path");
        ReplaceId(doc2, id, "new path");

        doc1.Save("xml1_new.xml");
        doc2.Save("xml2_new.xml");

        Console.WriteLine("Enter to exit...");
        Console.ReadLine();
    }

    private void ReplaceId(XDocument doc, string id, string newValue)
    {
        var ns = doc.Root.GetDefaultNamespace();
        var assetElements = doc.Descendants(ns + "Asset");

        foreach (var element in assetElements)
        {
            var idElement = element.Descendants(ns + "Id").First();
            if (!idElement.Value.Equals(id))
                continue;

            // for xml model #1
            SetNewValue(element, ns + "Path", newValue);

            // for xml model #2
            SetNewValue(element, ns + "OriginalFileName", newValue);
        }
    }

    private void SetNewValue(XElement currentElement, XName elementName, string newValue)
    {
        var matchingElements = currentElement.Descendants(elementName);
        if (matchingElements.Any())
        {
            foreach (var element in matchingElements)
                element.SetValue(newValue);
        }
    }

现在让我谈谈这件事。

更新

对ReplaceId的修改以了解是否已完成替换:

    private bool ReplaceId(XDocument doc, string id, string newValue)
    {
        var ns = doc.Root.GetDefaultNamespace();
        var assetElements = doc.Descendants(ns + "Asset");
        var result = false;

        foreach (var element in assetElements)
        {
            var idElement = element.Descendants(ns + "Id").First();
            if (!idElement.Value.Equals(id))
                continue;

            // for xml model #1
            SetNewValue(element, ns + "Path", newValue);

            // for xml model #2
            SetNewValue(element, ns + "OriginalFileName", newValue);

            result = true;
        }

        return result;
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM