How to replace sub elements in xml files using c# - c#

I have 2 xml files:
File 1
<xml version.....>
<System>
<General>
<Instrument>
<Specific>
<Name>...</Name>
</Specific>
<Instrument>
</General>
<System>
File 2
<System>
<Specific>
<Name>...</Name>
<age>...</age>
....
</Specific>
<System>
File 1 only has one entry under the element Specific, File 2 has multiple entries under the element specific. I need to replace all the entries under Specific in File 1 with the entries under Specific in File 2.
How is this be done in c#, using System.Xml.Linq or System.Xml???

Good day.
You can do that iteratively adding nodes from one element to another like this using LinqToXml.
using System.Xml.Linq;
namespace XmlReplacer
{
class Program
{
static void Main(string[] args)
{
var doc1 = XDocument.Load(#"d:\temp\file1.xml");
var doc2 = XDocument.Load(#"d:\temp\file2.xml");
var specElement1 = doc1.Root.Element("General").Element("Instrument").Element("Specific");
var specElement2 = doc2.Root.Element("Specific");
specElement1.RemoveAll();
foreach (var xElement in specElement2.Elements())
{
specElement1.Add(xElement);
}
doc1.Save(#"d:\temp\file3.xml");
}
}
}
Result is file3.xml:
<?xml version="1.0" encoding="utf-8"?>
<System>
<General>
<Instrument>
<Specific>
<Name>Test2</Name>
<Age>10</Age>
</Specific>
</Instrument>
</General>
</System>

Related

Unable to get element in XML

<?xml version="1.0" encoding="utf-8"?>
<serv:message
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:serv="http://www.webex.com/schemas/2002/06/service" xsi:schemaLocation="http://www.webex.com/schemas/2002/06/service http://www.webex.com/schemas/2002/06/service.xsd">
<header>
<securityContext>
<webExID/>
<password/>
<siteID/>
<partnerID/>
</securityContext>
</header>
<body>
<bodyContent xsi:type="java:com.webex.service.binding.training.CreateTrainingSession"
xmlns="http://www.webex.com/schemas/2002/06/service/training"
xmlns:com="http://www.webex.com/schemas/2002/06/common"
xmlns:sess="http://www.webex.com/schemas/2002/06/session"
xmlns:serv="http://www.webex.com/schemas/2002/06/service"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.webex.com/schemas/2002/06/service/training http://www.webex.com/schemas/2002/06/service/training/trainingsession.xsd">
<sess:accessControl>
<sess:sessionPassword/>
</sess:accessControl>
<sess:schedule></sess:schedule>
<metaData>
<sess:confName/>
<agenda/>
<description/>
<greeting/>
<location/>
</metaData>
<enableOptions>
<chat/>
<poll/>
<audioVideo/>
<fileShare/>
<applicationShare/>
<desktopShare/>
<annotation/>
<fullScreen/>
<voip/>
</enableOptions>
</bodyContent>
</body>
</serv:message>
Above XML is standard VILT Create Event xml and I need to populate it with proper data.
The issue is I am able to get "securityContent" element using below code and node holds total count of child elements that is 4:
var node = xmlDoc.SelectNodes("/serv:message/header/securityContext/*", GetNameSpace(xmlDoc.NameTable));
But when I try to get another node i.e. "metaData" then I get Count 0 and way to getting element is exactly same except the path to the element.
Below is sample code that I've tried but not working:
static void Main(string[] args)
{
var xmlPathh = #"C:\Users\SKMEENA\Desktop\VILT.xml";// this holds above xml
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlPathh);
var node = xmlDoc.SelectNodes("/serv:message/header/securityContext/*", GetNameSpace(xmlDoc.NameTable));
var member = xmlDoc.SelectNodes("/serv:message/body/bodyContent/metaData/*", GetNameSpace(xmlDoc.NameTable));
}
static XmlNamespaceManager GetNameSpace(XmlNameTable objNameTable)
{
XmlNamespaceManager objNsManager =new XmlNamespaceManager(objNameTable);
objNsManager.AddNamespace("serv", "http://www.webex.com/schemas/2002/06/service");
objNsManager.AddNamespace("ns1", "http://www.webex.com/schemas/2002/06/service/site");
return objNsManager;
}
Anybody has any idea what is wrong with above code and how can I make it working?
The bodyContent node has a default namespace which applies to it and all its children.
You need to add it to the NamespaceManager and then use it in the XPath
var member = xmlDoc.SelectSingleNode("/serv:message/body/body:bodyContent/body:metaData", GetNameSpace(xmlDoc.NameTable));
member.OuterXml.Dump();
static XmlNamespaceManager GetNameSpace(XmlNameTable objNameTable)
{
XmlNamespaceManager objNsManager =new XmlNamespaceManager(objNameTable);
objNsManager.AddNamespace("serv", "http://www.webex.com/schemas/2002/06/service");
objNsManager.AddNamespace("ns1", "http://www.webex.com/schemas/2002/06/service/site");
objNsManager.AddNamespace("body", "http://www.webex.com/schemas/2002/06/service/training");
objNsManager.AddNamespace("sess","http://www.webex.com/schemas/2002/06/session");
return objNsManager;
}
dotnetfiddle

How can I display XML data using C#

I am trying to display the id attribute of the channel element called id, the inner text of the display-name tag and the inner text of the icon that sometimes is contained inside the channel element.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE tv SYSTEM "xmltv.dtd">
<tv generator-info-name="xmltv.co.uk" source-info-name="xmltv.co.uk">
<channel id="0052a71acac348ff93f5680aa9c125eb">
<display-name>2910</display-name>
</channel>
<channel id="00da025711e82cf319cb488d5988c099">
<display-name>Sony Movies</display-name>
</channel>
<channel id="00dfea977320f17bb419abaa1f079f39">
<display-name>Good Food</display-name>
<icon src="/images/channels/00dfea977320f17bb419abaa1f079f39.png"/>
</channel>
<channel id="018202232e044b504f9dc5263617d496">
<display-name>The Box</display-name>
<icon src="/images/channels/018202232e044b504f9dc5263617d496.png"/>
</channel>
I tried using this code C# code below But the second if give me a error about not referenced to an object.
XmlDocument doc = new XmlDocument();
doc.Load(xmlLocation);
//dispaly the nodes
foreach (XmlNode node in doc.DocumentElement.ChildNodes)
{
//get the channel
if (node.Name.Equals("channel"))
{
Debug.WriteLine("Channel Name : " + node.ChildNodes[0].Name.ToString()); //or loop through its children as well
//Debug.WriteLine("Channel Name : " + node.AttributeCount.ToString()); //or loop through its children as well
//get the icon element
if(node.ChildNodes[1].Name != null)
Debug.WriteLine("Channel Name : " + node.ChildNodes[1].Name.ToString());
}
}
Although XDocument/XElement and LinQ to XML is the new trend,
to follow your implementation, and adding to it only one feature (using XPATH to query document contents);
Please find the code to fetch channel names and their respective icon source URL's (if exist)
By applying SelectNodes and SelectSingleNode, the API is iterating over the nodes for us.
// Select all the XML elements whose name is "channel"
foreach (XmlNode channelNode in doc.DocumentElement.SelectNodes("channel"))
{
// check if a child element with the name "display-name" exists
XmlNode displayNameNode = channelNode.SelectSingleNode("display-name");
if (displayNameNode != null)
{
// If yes, print the inner text
Debug.WriteLine("Channel Name : " + displayNameNode.InnerText);
}
// then check if the icon node exists
XmlNode iconNode = channelNode.SelectSingleNode("icon");
if (iconNode != null)
{
// and check if it has an attribute with the name "src"
if (iconNode.Attributes["src"] != null)
{
// and if yes, print out its value
Debug.WriteLine(" Icon Src : " + iconNode.Attributes["src"].Value);
}
}
}
First, you need to convert string to XML and load them up in XmlDocument and then use the XPath as shown below. The simple program you can run that in dotnetfiddle.net to check this out.
using System;
using System.Xml;
public class Program
{
public static void Main()
{
string xmlString = "<tv generator-info-name='xmltv.co.uk' source-info-name='xmltv.co.uk'> <channel id='0052a71acac348ff93f5680aa9c125eb'> <display-name>2910</display-name> </channel> <channel id='00da025711e82cf319cb488d5988c099'> <display-name>Sony Movies</display-name> </channel> <channel id='00dfea977320f17bb419abaa1f079f39'> <display-name>Good Food</display-name> <icon src='/images/channels/00dfea977320f17bb419abaa1f079f39.png'/> </channel> <channel id='018202232e044b504f9dc5263617d496'> <display-name>The Box</display-name> <icon src='/images/channels/018202232e044b504f9dc5263617d496.png'/> </channel></tv>";
XmlDocument xmltest = new XmlDocument();
xmltest.LoadXml(xmlString);
XmlNodeList itemNodes = xmltest.SelectNodes("//tv/channel");
foreach(XmlNode itemNode in itemNodes)
{
if (itemNode!= null) {
Console.WriteLine(string.Format("Id:{0}", (itemNode as XmlElement).GetAttribute("id")));
}
}
}
}

c# Linq XML - Why Is Whitespace Being Added to Elements With Quotes / Namespace?

A portion of my c# .NET program contains code to modify elements within an XML document. The code works fine in terms of modifying the values based on the variables I'm setting elsewhere in the code, but the problem is that whitespace is being added to all of the the elements when the xml file is saved with the updates.
I am not accessing this element at all in my code. I am assuming that it's because of the quotation marks for the algorithm namespace value, because I can't see any other reason why this would happen. I am using Preserve Namespace on load, and Disable Formatting on save.
So question is, why is it adding this extra whitespace, and how can I stop it?
XML (Source File)
<?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>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>
</AssetList>
</PackingList>
XML (Output File)
<?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>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>
</AssetList>
</PackingList>
Code (partial):
XDocument pkldoc = XDocument.Load(packing, LoadOptions.PreserveWhitespace);
var pklns = pkldoc.Root.GetDefaultNamespace();
var pkluuid = pkldoc.Descendants(pklns + "Id").FirstOrDefault().Value;
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, SaveOptions.DisableFormatting);
FileInfo fi = new FileInfo(packing);
var pklsize = fi.Length;
This works, though not very clean on my part.
string text = File.ReadAllText(packing);
text = text.Replace(" />", "/>");
File.WriteAllText(packing, text);
UPDATE
This is the solution. Thanks you #asherber !
var textToSave = pkldoc.ToString(SaveOptions.DisableFormatting).Replace(" />", "/>");
File.WriteAllText(packing, textToSave);

XML C# Parse - Complex

I have been on google for a while but am just stumped. I need to parse xml of this nature. I can't seem to skip to elements in the middle, e.g. Folder. I have limited the xml as there were many 'Folder' elements. Any guidance would be appreciated. I was after the FolderID element's attribute ID.
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorVersion="14"
MinorVersion="1"
MajorBuildNumber="225"
MinorBuildNumber="46"
Version="Exchange2010_SP1"
xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns="http://schemas.microsoft.com/exchange/services/2006/types"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" />
</s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<m:GetFolderResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:GetFolderResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Folders>
<t:Folder>
<t:FolderId Id="AAMkADk5MmY1ZThmLTM2MzAtNGVh" ChangeKey="AQAAABYAAACSe/NBrSZiQKqHx8yL+lIRAAAA1EWM" />
<t:ParentFolderId Id="AAMkADk5MmY1ZThmLTM2MzAtNGVh" ChangeKey="AQAAAA==" />
<t:DisplayName>Top of Information Store</t:DisplayName>
<t:TotalCount>0</t:TotalCount>
<t:ChildFolderCount>15</t:ChildFolderCount>
<t:EffectiveRights>
<t:CreateAssociated>true</t:CreateAssociated>
<t:CreateContents>true</t:CreateContents>
<t:CreateHierarchy>true</t:CreateHierarchy>
<t:Delete>true</t:Delete>
<t:Modify>true</t:Modify>
<t:Read>true</t:Read>
<t:ViewPrivateItems>true</t:ViewPrivateItems>
</t:EffectiveRights>
<t:PermissionSet>
<t:Permissions>
<t:Permission>
<t:UserId>
<t:DistinguishedUser>Default</t:DistinguishedUser>
</t:UserId>
<t:CanCreateItems>false</t:CanCreateItems>
<t:CanCreateSubFolders>false</t:CanCreateSubFolders>
<t:IsFolderOwner>false</t:IsFolderOwner>
<t:IsFolderVisible>false</t:IsFolderVisible>
<t:IsFolderContact>false</t:IsFolderContact>
<t:EditItems>None</t:EditItems>
<t:DeleteItems>None</t:DeleteItems>
<t:ReadItems>None</t:ReadItems>
<t:PermissionLevel>None</t:PermissionLevel>
</t:Permission>
<t:Permission>
<t:UserId>
<t:DistinguishedUser>Anonymous</t:DistinguishedUser>
</t:UserId>
<t:CanCreateItems>false</t:CanCreateItems>
<t:CanCreateSubFolders>false</t:CanCreateSubFolders>
<t:IsFolderOwner>false</t:IsFolderOwner>
<t:IsFolderVisible>false</t:IsFolderVisible>
<t:IsFolderContact>false</t:IsFolderContact>
<t:EditItems>None</t:EditItems>
<t:DeleteItems>None</t:DeleteItems>
<t:ReadItems>None</t:ReadItems>
<t:PermissionLevel>None</t:PermissionLevel>
</t:Permission>
</t:Permissions>
</t:PermissionSet>
<t:UnreadCount>0</t:UnreadCount>
</t:Folder>
</m:Folders>
</m:GetFolderResponseMessage>
</m:ResponseMessages>
</m:GetFolderResponse>
</s:Body>
</s:Envelope>
LINQ to XML is the best .NET XML Parsing API.
See
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/xdocument-class-overview
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace ConsoleApp12
{
class Program
{
static void Main(string[] args)
{
var doc = XDocument.Load(#"c:\temp\foo.xml");
var ns = (XNamespace)"http://schemas.microsoft.com/exchange/services/2006/types";
var folders = doc.Descendants(ns + "Folder");
foreach (var e in folders)
{
var folderId = e.Element(ns + "FolderId").Attribute("Id").Value;
Console.WriteLine(folderId);
}
Console.WriteLine("Hit any key to exit.");
Console.ReadKey();
}
}
}
You should learn about serlialization. It's very easy to convert an XML to and from a object in C#. https://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part
That said, this will get you the data you're after. It's not very reusable and won't help you with any xml files other than ones with one instance of that attribute, but it's what you're after so here you go.
string FolderId;
string ChangeKey;
using (StreamReader sr = new StreamReader("c:\\myfile.xml"))
{
string line;
while ((line = sr.ReadLine()) != null)
{
if (line.Contains("<t:FolderId Id="))
{
try
{
var lineArray = line.Split('\"');
FolderId = lineArray[1];
ChangeKey = lineArray[3];
}
catch
{
// handle exception
}
}
}
}
You can use the XSD.exe to create a schema class. and then using XML deserializer, you can deserialize/parse xml to object
Using xml linq
XDocument doc = XDocument.Load(FILENAME);
List<XElement> folders = doc.Descendants().Where(x => x.Name.LocalName == "Folder").ToList();
XNamespace tNs = folders.FirstOrDefault().GetNamespaceOfPrefix("t");
XElement id_AAMkADk5MmY1ZThmLTM2MzAtNGVh = folders.Where(x => (string)x.Element(tNs + "FolderId").Attribute("Id") == "AAMkADk5MmY1ZThmLTM2MzAtNGVh").FirstOrDefault();

Add Namespace to xml file

I have a xml file to which I want to add predefined namespeces.. Following is the code:
private const string uri = "http://www.w3.org/TR/html4/";
private static readonly List<string> namespaces = new List<string> { "lun" };
public static XElement AddNameSpaceAndLoadXml(string xmlFile) {
var nameSpaceManager = new XmlNamespaceManager(new NameTable());
// add custom namespace to the manager and take the prefix from the collection
namespaces.ToList().ForEach(name => {
nameSpaceManager.AddNamespace(name, string.Concat(uri, name));
});
XmlParserContext parserContext = new XmlParserContext(null, nameSpaceManager, null, XmlSpace.Default);
using (var reader = XmlReader.Create(#xmlFile, null, parserContext)) {
return XElement.Load(reader);
}
}
The problem is that the resulting xml in memory does not show the correct namespaces added. Also, they are not added at the root but are added next to the tag. Xml added below.
In the xml it is showing p3:read_data while should be lun:read_data.
How do i get to add the namespace on the root tag and not get the incorrect name.
Sample Input xml:
<config file-suffix="perf">
<overview-graph title="Top 5 LUN Reads" max-series="5" remove-series="1">
<counters lun:read_data=""/>
</overview-graph>
</config>
Output xml expected:
<config file-suffix="perf" xmlns:lun="http://www.w3.org/TR/html4/lun">
<overview-graph title="Top 5 LUN Reads" max-series="5" remove-series="1">
<counters lun:read_data="" />
</overview-graph>
</config>
Output that is coming using the above code:
<config file-suffix="perf" >
<overview-graph title="Top 5 LUN Reads" max-series="5" remove-series="1">
<counters p3:read_data="" xmlns:p3="http://www.w3.org/TR/html4/lun"/>
</overview-graph>
</config>
I am not sure if there is a better way, but adding the namespace manually seems to work.
using (var reader = XmlReader.Create(#xmlFile, null, parserContext)) {
var newElement = XElement.Load(reader);
newElement.Add(new XAttribute(XNamespace.Xmlns + "lun", string.Concat(uri, "lun")));
return newElement;
}
I don't know offhand a way to generalize this however (obviously you can add the whole set by enumerating it, but outputting only used namespaces might be interesting).

Categories