使用C#XML文档类修改子节点值
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用C#XML文档类修改子节点值相关的知识,希望对你有一定的参考价值。
我是一个总的菜鸟,所以道歉,但我到处搜寻,我仍然被卡住了。
我正在编写一个程序,在用户选择初始XML文档并重命名后,将修改2个XML文档。一旦用户更改了第一个XML的文件名(让我们称之为XML#1),其他两个XML文档必须将新文件名插入其中的特定节点值(XML#2和#3)。
每个XML文档都有多个具有完全相同名称的“资产”节点,我需要通过使用XML Doc#1中存在的唯一UUID值来区分我想要的节点。
对于XML#2和#3,“Id”节点包含相同的唯一UUID。我正在解析XML#1以获取此UUID,并将其分配给名为“cpluuid”的变量。
然后我在XML#2和#3中搜索“Id”=“cpluuid”的节点,并尝试修改包含要插入的文件名的正确节点。
XML Doc#2 - aka var = packing(省略了额外的“Asset”节点)
<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 doc#3的问题,因为我需要修改的节点(“Path”)嵌套在树中:
XML Doc#3 - aka 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”不是“资产”的兄弟。此代码不会修改“Path”值。我尝试使用不同的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
基于@Juan M. Vergara的精彩反馈,我第二次尝试使用XDocument。遗憾的是,虽然代码执行时没有错误,但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的较大部分。需要更新的“Path”节点是树中的第二个节点:
<?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”不是“资产”下的“第一”元素。我正在尝试不同的语法组合,但似乎没有任何工作(序列不包含元素错误)
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);
由于新的条件,我更愿意写一个不同的答案。鉴于你有这两个样本xmls,具有不同的结构但具有共同的节点,我已经使用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);
}
}
现在让我谈谈这个问题。
UPDATE
对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;
}
我只是尝试了一下并在控制台应用程序示例中汇总了一些代码,请记住我不能控制所有错误(例如找不到元素......),但我认为它可以给你一个想法如何使用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 osomefilename.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 osomefilename.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();
}
我希望这次你得到正确答案:)。
以上是关于使用C#XML文档类修改子节点值的主要内容,如果未能解决你的问题,请参考以下文章