简体   繁体   English

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

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

I am a total noob, so apologies, but I have searched everywhere and I'm still stuck. 我真是个菜鸟,很抱歉,但是我到处搜索了,但仍然受困。

I am writing a program which will modify 2 XML docs after the user has selected an initial XML document and renamed it. 我正在编写一个程序,该程序将在用户选择初始XML文档并将其重命名后修改2个XML文档。 Once the user changes the filename of the first XML (let's call it XML #1), the other two XML docs must have the new filename inserted into specific node values within them (XML #2 and #3). 一旦用户更改了第一个XML的文件名(我们将其称为XML#1),其他两个XML文档必须将新文件名插入其中的特定节点值(XML#2和#3)。

Each XML doc has multiple "Asset" nodes with exactly the same names, and I need to distinguish the node I want by using a unique UUID value that is present in XML Doc #1. 每个XML文档都具有名称完全相同的多个“资产”节点,我需要使用XML文档#1中存在的唯一UUID值来区分所需的节点。

For both XML #2 and #3, the "Id" nodes contain this same unique UUID. 对于XML#2和#3,“ Id”节点都包含相同的唯一UUID。 I am parsing XML#1 to get this UUID, and assigning it to a variable called "cpluuid." 我正在解析XML#1以获取此UUID,并将其分配给名为“ cpluuid”的变量。

Then I am searching XML #2 and #3 for nodes with an "Id" = to "cpluuid", and attempting to modify the correct node that contains the file name to be inserted. 然后,我在XML#2和#3中搜索“ Id” =到“ cpluuid”的节点,并尝试修改包含要插入文件名的正确节点。

XML Doc #2 - aka var = packing (additional "Asset" nodes omitted) 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>

I have had success with updating XML Doc #2 in this manner. 我已经成功地以这种方式更新了XML Doc#2。 The "OriginalFileName" node was successfully updated to the variable value "cpluuid" via this code: 通过以下代码,“ 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);

However, I am having trouble with XML doc #3, because the node I need to modify ("Path") is nested further down in the tree: 但是,我在使用XML文档#3时遇到了麻烦,因为我需要修改的节点(“路径”)嵌套在树的更下方:

XML Doc #3 - aka var = assetmap 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>

So I tried using similar code, but cannot figure out how to drill down further since "Path" is not a sibling of "Asset." 因此,我尝试使用类似的代码,但由于“ Path”不是“ Asset”的同级兄弟,因此无法弄清楚如何进一步深入研究。 This code does not modify the "Path" value. 此代码不会修改“路径”值。 I tried using different XPath syntax, but nothing I know works: 我尝试使用不同的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);

I have tried various using System.Xml.Linq methods as well, but cannot get this to work. 我也尝试过使用System.Xml.Linq方法进行各种操作,但无法使其正常工作。

UPDATE#1 更新#1

Here is how I have been attempting to use XDocument. 这就是我一直尝试使用XDocument的方式。 Seems like it should work, but always returns a Object Reference error. 似乎应该可以,但是总是返回对象引用错误。

var xml = XDocument.Load(assetmap);

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

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

UPDATE #2 更新#2

My second attempt using XDocument based on awesome feedback from @Juan M. Vergara. 我第二次尝试使用XDocument是基于@Juan M.Vergara的出色反馈。 Unfortunately, while the code executes with no errors, the XML node does not update to the new value. 不幸的是,虽然代码没有错误地执行,但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);

Here is a lager portion of the XML as well, in case there is an issue with namespaces or something else. 如果名称空间或其他内容有问题,这也是XML的较大部分。 The "Path" node that nneeds to be updated is the second one in the tree: 需要更新的“路径”节点是树中的第二个节点:

<?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>

UPDATE #3 更新#3

I'm trying the same method for XML#2 now, but it's not working. 我现在正在为XML#2尝试相同的方法,但是没有用。 I assume because "OriginalFileName" is not the "First" element under "Asset." 我认为是因为“ OriginalFileName”不是“ Asset”下的“ First”元素。 I'm trying different syntax combos, but nothing seems to work (sequence contains no element errors) 我正在尝试使用不同的语法组合,但是似乎没有任何效果(顺序不包含任何元素错误)

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);
     }
    }

UPDATE #4 更新#4

Here are the whole contents of XML #2. 这是XML#2的全部内容。 The structure is different than XML#3. 结构与XML#3不同。 I only want to update the "OriginalFileName" element in the very last "Asset" tree. 我只想更新最后一个“资产”树中的“ 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>

UPDATE #5 更新#5

Thanks again Juan for your help. 再次感谢Juan的帮助。 Here is the working solution for XML Doc #2. 这是XML Doc#2的工作解决方案。 I know it's not very pretty, but it works. 我知道它不是很漂亮,但是可以。

At some point I'd like to go back and fully digest your last example and combine / cleanup my code the way you have. 在某个时候,我想返回并完全消化您的最后一个示例,并按照您的方式组合/清理我的代码。 THANKS! 谢谢!

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);

I just gave it a try and put together some code in a console application example, bear in mind that I do not control all errors in al cases (like element not found...), but I think that it can give you an idea of how you can use System.Xml.Linq. 我只是尝试了一下,然后在控制台应用程序示例中整理了一些代码,请记住,在某些情况下我不控制所有错误(例如找不到元素...),但是我认为它可以给您一个思路如何使用System.Xml.Linq。

Hope it helps. 希望能帮助到你。

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();
        }
    }
}

And this is the output I get: 这是我得到的输出:

<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...

Update 更新资料

Problem is that there are more than one Asset, and as the first one is not matching the first foreach gives a message and stops execution in the return, just replace the return statement by a continue instruction, so the execution does not stop. 问题是资产不止一个,并且由于第一个资产不匹配,因此第一个foreach会给出一条消息并在返回中停止执行,只需用continue指令替换return语句,这样执行就不会停止。

In the other hand, you may have to include tha namespace, this is the updated code of my initial ReplacePath function: 另一方面,您可能必须包括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();
    }

I hope this time you got the right answer :). 希望这次您得到正确的答案:)。

I preferred to write a different answer, due to the new conditions. 由于新的条件,我更愿意写一个不同的答案。 Given you have those 2 sample xmls, with different structures but with common nodes, I have given another try using Descendants. 假设您有这2个示例xml,它们具有不同的结构但具有共同的节点,我又尝试了使用Descendants。

You basically need to find the Asset elements, then check whether the Id matches, and if so, update the Path or OriginalFileName element, try this code: 您基本上需要找到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);
        }
    }

Let me now how it goes with this one. 现在让我谈谈这件事。

UPDATE 更新

A modification to ReplaceId to know if a replacement has been done: 对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