How to compare two XmlNodes of XmlDocument in C#? - c#

This is a sample xml. If a new font is to be added in the sense, all the existing fonts to be compare with the new font before adding to the preference.
How do I check the node(font) whether already exists or not in case of XmlDocument?
<preferences>
<font role="console">
<size>9</size>
<fname>Courier</fname>
</font>
<font role="default">
<fname>Times New Roman</fname>
<size>14</size>
</font>
<font role="titles">
<size>10</size>
<fname>Helvetica</fname>
</font>
</preferences>

Xnode has a feature called DeepEqual to compare nodes. XmlNode can be converted into Xnode by simple parsing.
XElement XmlElementToXElement(XmlNode e)
{
return XElement.Parse(e.OuterXml);
}
So, it will be easier from here. The method below will return true if two nodes are equal.
bool XmlNode_DeepEquals(XmlNode node1, XmlNode node2)
{
XElement tempX = XmlElementToXElement(node2);
XElement searchX = XmlElementToXElement(node1);
return XNode.DeepEquals(tempX, searchX))
}
This method is for comparing a list of Node.
bool isNodeAlreadyExists(XmlNode searchNode, XmlNodeList list)
{
bool exists = false;
foreach (XmlNode node in list)
{
if (XmlNode_DeepEquals(searchNode, node))
{
exists = true;
}
}
return exists;
}

One approach would be to create a couple of classes to represent the XML document and implement the IEquatable<T> Interface.
https://dotnetfiddle.net/QZFwDy
Classes for XML
[XmlRoot(ElementName = "font")]
public class Font : IEquatable<Font>
{
[XmlElement(ElementName = "size")]
public string Size { get; set; }
[XmlElement(ElementName = "fname")]
public string Fname { get; set; }
[XmlAttribute(AttributeName = "role")]
public string Role { get; set; }
public bool Equals(Font font)
{
if (font == null) return false;
return (Size == font.Size) && (Fname == font.Fname) && (Role == font.Role);
}
}
[XmlRoot(ElementName = "preferences")]
public class Preferences
{
[XmlElement(ElementName = "font")]
public List<Font> Font { get; set; }
}
Then use the Preferences class to deserialize the XML. Once the document is deserialized, leverage the List<T>.Contains(T item) method to see if the font node exists. The .Contains method will call the implementation of Equals in the Font class.
Code to Demonstrate
static void Main(string[] args)
{
Preferences preferences = null;
var xmlString = Data.XML;
using (var stream = new StringReader(xmlString))
{
var serializer = new XmlSerializer(typeof(Preferences));
preferences = (Preferences)serializer.Deserialize(stream);
}
var node0 = new Font()
{
Fname = "New One",
Role = "console",
Size = "12"
};
var node1 = new Font()
{
Fname = "Helvetica",
Role = "titles",
Size = "10"
};
if (preferences.Font.Contains(node0))
{
// Not expecting to enter here
Console.WriteLine($"'{nameof(node0)}' is already present");
}
if (preferences.Font.Contains(node1))
{
Console.WriteLine($"'{nameof(node1)}' is already present");
}
}

Related

Too many <?xml in one Xml file

I was finally able to create a Xml file with some coding (with lots of help here from some people). I can store the file but the next process is reading it back. This gave me an error:
"Unexpected Xml declaration". After searching a bit I understand that a Xml file can have only one time <?xml version=1.0 etc>. But my document has several of these statements in the document. And therefore the code
xmlDocument doc = new XmlDocument
Throws me this error. The question is how do I get rid of all these comments in the xml document.
The document is create with 2 functions:
private void btnSave_Click(object sender, EventArgs e)
{
//Check if all fields are filled in
if (txbCompany.Text == "" || txbSiteName.Text == "" || txbIMO.Text == "")
{
MessageBox.Show("Please fill in all empty fields");
}
else if (NumMachTot.Value == 0)
{
MessageBox.Show("A Client profile needs at least one assigned machine!");
}
else
{
var appData = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "VibroManager");
Directory.CreateDirectory(appData);
//Create the Company Profile Class
CompanyProfile companyProfile = new CompanyProfile();
companyProfile.CompanyName = txbCompany.Text;
companyProfile.SiteName = txbSiteName.Text;
companyProfile.Imo = txbIMO.Text;
companyProfile.MachineTotal = (int)NumMachTot.Value;
//Serialization of the companyProfile and append to the document
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(CompanyProfile));
using (var writer = new StreamWriter(Path.Combine(appData, $"{txbCompany.Text}_{txbSiteName.Text}.xml"), true))
{
x.Serialize(writer, companyProfile);
}
//Iterate the datasource list, NOT the DataGridView.
foreach (MachineProfile machineProfile in dsMachineProfile)
{
AppendMachineData(machineProfile, fileName: Path.Combine(appData, $"{txbCompany.Text}_{txbSiteName.Text}.xml"));
}
//Close form and open Main form
this.Hide();
frmMain fMain = new frmMain();
fMain.ShowDialog();
}
}
changed Code:
private void AppendMachineData(MachineProfile machineProfile, string fileName)
{
//Serialization of the MachineProle and append to the document
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(MachineProfile));
var settings = new XmlWriterSettings();
using (var writer = new StreamWriter(fileName, true))
{
settings.Indent = true;
settings.OmitXmlDeclaration = true;
x.Serialize(writer, machineProfile);
}
}
I think in these two functions the problem is created but in fact I do not know why and how. Or maybe there is another way to solve this.
This is the code that I use to read the xml file
private void openToolStripMenuItem_Click(object sender, EventArgs e)
{
var filePath = string.Empty;
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData );
openFileDialog.Filter = "All files (*.*)|*.*";
openFileDialog.FilterIndex = 2;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
//Get the path of specified file
filePath = openFileDialog.FileName;
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlNode node = doc.DocumentElement.SelectSingleNode("/CompanyName");
lblCompanyName.Text = node.InnerText;
}
}
}
Here is a screenshot of the Xml file
Below shows how to use XML serialization which allows one to use classes to store one's data and then serialize these classes to save the data to an XML file. Deserialization reads the data from the XML file and populates the classes.
Try the following:
To each of the classes below, add the following using statements
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
Create a class (name: XmlCompanyProfile.cs)
public class XmlCompanyProfile
{
public string CompanyName { get; set; }
public string SiteName { get; set; }
public int Imo { get; set; }
public int MachineTotal { get; set; }
}
If an element name isn't specified, the name of the property is used in the XML file.
Example 1:
public string CompanyName { get; set; }
By specifying an ElementName, one can make the property name different than the name used in the XML file.
Example 2:
[XmlElement(ElementName = "Name")]
public string CompanyName { get; set; }
Of course, one can also set the value of ElementName to the property name.
Example 3:
[XmlElement(ElementName = "CompanyName")]
public string CompanyName { get; set; }
Note: In Example 3, since the element name is set to the same value as the property name, it will have the same result as Example 1.
If one wishes to specify the element names, the XmlCompanyProfile class will look like the following instead:
public class XmlCompanyProfile
{
[XmlElement(ElementName = "CompanyName")]
public string CompanyName { get; set; }
[XmlElement(ElementName = "SiteName")]
public string SiteName { get; set; }
[XmlElement(ElementName = "Imo")]
public int Imo { get; set; }
[XmlElement(ElementName = "MachineTotal")]
public int MachineTotal { get; set; }
}
Create a class (name: XmlMachineProfile.cs)
public class XmlMachineProfile
{
[XmlElement(ElementName = "MachineName")]
public string MachineName { get; set; }
[XmlElement(ElementName = "NominalSpeed")]
public int NominalSpeed { get; set; }
}
Create a class (name: XmlRoot.cs)
[XmlRoot(ElementName = "Root")]
public class XmlRoot
{
[XmlElement(ElementName = "CompanyProfile")]
public XmlCompanyProfile CompanyProfile { get; set; } = new XmlCompanyProfile();
[XmlElement(ElementName = "MachineProfile")]
public List<XmlMachineProfile> MachineProfiles { get; set; } = new List<XmlMachineProfile>();
}
To serialize and deserialize the data:
Create a class (name: HelperXml.cs)
public static class HelperXml
{
public static T DeserializeXMLFileToObject<T>(string xmlFilename)
{
//Usage: Class1 myClass1 = DeserializeXMLFileToObject<Class1>(xmlFilename);
T rObject = default(T);
if (string.IsNullOrEmpty(xmlFilename))
{
throw new Exception($"Error: XML filename not specified (xmlFilename: '{xmlFilename}'");
}
using (System.IO.StreamReader xmlStream = new System.IO.StreamReader(xmlFilename))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
rObject = (T)serializer.Deserialize(xmlStream);
}
return rObject;
}
public static void SerializeObjectToXMLFile(object obj, string xmlFilename)
{
//Usage: Class1 myClass1 = new Class1();
//SerializeObjectToXMLFile(myClass1, xmlFilename);
if (string.IsNullOrEmpty(xmlFilename))
{
throw new Exception($"Error: XML filename not specified (xmlFilename: '{xmlFilename}'");
}
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings() { Indent = true, OmitXmlDeclaration = true};
using (System.Xml.XmlWriter xmlWriter = System.Xml.XmlWriter.Create(xmlFilename, settings))
{
//specify namespaces
System.Xml.Serialization.XmlSerializerNamespaces ns = new System.Xml.Serialization.XmlSerializerNamespaces();
ns.Add(string.Empty, "urn:none"); //eliminates "xsd" and "xsi" namespaces
//create new instance
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
//serialize (write to XML file)
serializer.Serialize(xmlWriter, obj, ns);
}
}
}
Usage (serialization):
Note: In the code below, the name of the button is btnSerialize.
private XmlRoot _root = null;
...
private void btnSerialize_Click(object sender, EventArgs e)
{
//create new instance
_root = new XmlRoot();
//set CompanyProfile info
_root.CompanyProfile.CompanyName = "ABB";
_root.CompanyProfile.SiteName = "Rotterdam";
_root.CompanyProfile.Imo = 123456;
_root.CompanyProfile.MachineTotal = 3;
//add machine profile
_root.MachineProfiles.Add(new XmlMachineProfile() { MachineName = "Machine 1", NominalSpeed = 50 });
_root.MachineProfiles.Add(new XmlMachineProfile() { MachineName = "Machine 2", NominalSpeed = 60 });
_root.MachineProfiles.Add(new XmlMachineProfile() { MachineName = "Machine 3", NominalSpeed = 50 });
//prompt user for XML filename
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.FileName = "Test.xml"; //default filename
sfd.Filter = "XML File (*.xml)|*.xml";
sfd.Title = "Save XML file";
if (sfd.ShowDialog() == DialogResult.OK)
{
//serialize
HelperXml.SerializeObjectToXMLFile(_root, sfd.FileName);
}
}
}
Usage (deserialization):
Note: In the code below, the name of the button is btnDeserialize.
private XmlRoot _root = null;
...
private void btnDeserialize_Click(object sender, EventArgs e)
{
//prompt user for XML filename
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "XML File (*.xml)|*.xml";
ofd.Title = "Open XML file";
if (ofd.ShowDialog() == DialogResult.OK)
{
//deserialize
_root = HelperXml.DeserializeXMLFileToObject<XmlRoot>(ofd.FileName);
System.Diagnostics.Debug.WriteLine($"CompanyName: '{_root.CompanyProfile.CompanyName}'");
}
}
}
Here's what the XML file looks like:
<Root>
<CompanyProfile>
<CompanyName>ABB</CompanyName>
<SiteName>Rotterdam</SiteName>
<Imo>123456</Imo>
<MachineTotal>3</MachineTotal>
</CompanyProfile>
<MachineProfile>
<MachineName>Machine 1</MachineName>
<NominalSpeed>50</NominalSpeed>
</MachineProfile>
<MachineProfile>
<MachineName>Machine 2</MachineName>
<NominalSpeed>60</NominalSpeed>
</MachineProfile>
<MachineProfile>
<MachineName>Machine 3</MachineName>
<NominalSpeed>50</NominalSpeed>
</MachineProfile>
</Root>
Resources:
XML serialization
Examples of XML Serialization
XDocument Class
JSON Utils

file xml inside multiple xml in one line

I have a file .xml inside multiple xml in one line.
How can I read this file and convert to object?
I tried with this code it works if there is only one.
Please help and thank you all
[XmlRoot(ElementName = "DepartmentMaster")]
public class DepartmentMaster
{
[XmlElement(ElementName = "DepartmentId")]
public int DepartmentId { get; set; }
[XmlElement(ElementName = "Name")]
public string Name { get; set; }
[XmlElement(ElementName = "Description")]
public string Description { get; set; }
[XmlElement(ElementName = "test")]
public int Test { get; set; }
}
//string xml = "<DepartmentMaster><DepartmentId>267854</DepartmentId><Name>Purchase</Name><Description>Purchase Department</Description><test>1</test></DepartmentMaster>";
string xml = "<DepartmentMaster><DepartmentId>267854</DepartmentId><Name>Purchase</Name><Description>Purchase Department</Description><test>1</test></DepartmentMaster><DepartmentMaster><DepartmentId>267855</DepartmentId><Name>Purchase5</Name><Description>Purchase Department5</Description><test>5</test></DepartmentMaster>";
using (TextReader reader = new StringReader(xml))
{
System.Xml.Serialization.XmlSerializer deserializer = new System.Xml.Serialization.XmlSerializer(typeof(DepartmentMaster));
var model = (DepartmentMaster)deserializer.Deserialize(reader);
}
image from the database
image from the database
Here it is two approaches below.
The first is using setting to accept XML data with multiple root elements (ConformanceLevel.Fragment).
private static IList<DepartmentMaster> DeserializeFragment(string xml)
{
var settings = new XmlReaderSettings
{
ConformanceLevel = ConformanceLevel.Fragment
};
XmlReader reader = XmlReader.Create(new MemoryStream(Encoding.ASCII.GetBytes(xml)), settings);
var serializer = new XmlSerializer(typeof(DepartmentMaster));
var list = new List<DepartmentMaster>();
while (serializer.Deserialize(reader) is DepartmentMaster element)
{
list.Add(element);
}
return list;
}
And the second by adding a root element to deserialize a well-formed XML document.
public class DepartmentMasters
{
[XmlElement("DepartmentMaster")]
public List<DepartmentMaster> Items;
}
private static DepartmentMasters DeserializeWellFormedXML(string xml)
{
var text = #"<?xml version=""1.0""?><DepartmentMasters>" + xml + "</DepartmentMasters>";
var serializer = new XmlSerializer(typeof(DepartmentMasters));
return (DepartmentMasters)serializer.Deserialize(new StringReader(text));
}

Deserialising XML with different element name in c#

XML:
<?xml version="1.0" encoding="UTF-8"?>
<Images>
<I0>
<Path>123.com</Path>
<I0>
<I1>
<Path>123.com</Path>
<I1>
<I2>
<Path>123.com</Path>
<I2>
</Images>
Can serializer.Deserialize() be used to get tags with different names into a collection?
currently, in my object I have:
C#:
public class rootObject
{
[XmlElement(ElementName = "I0")]
public I0 I0 { get; set; }
[XmlElement(ElementName = "I1")]
public I1 I1 { get; set; }
[XmlElement(ElementName = "I2")]
public I2 I2 { get; set; }
}
But I would like to have (Because Images can have more or fewer elements):
public class rootObject
{
public List<I> Is { get; set; }
}
You can do what you are suggesting you just merely need to pass in the type argument in your class doing the generic. The key point to remember when you do a deserialization routine is that the routine needs to know the sub reference. So if I was to say string.Deserialize it would bomb. It would need to know a reference string.Deserialize> where Sub could be the class object that may change.
Say I have a base class and I want 'T' to be a type I can change for extensible abilities later.
[Serializable]
public class Test<T> where T : class
{
public Test() { }
public int TestId { get; set; }
public string Name { get; set; }
public List<T> Shipments { get; set; }
}
I want to test this with two classes I just make up that have different properties slightly
[Serializable]
public class Sub1
{
public int Id { get; set; }
public string Desc { get; set; }
}
[Serializable]
public class Sub2
{
public int IdWhatever { get; set; }
public string DescWhatever { get; set; }
}
Now let's do a main program and test serialization.
class Program
{
static void Main(string[] args)
{
var serializeTest = new Test<Sub1> { TestId = 1, Name = "Test", Shipments = new List<Sub1> { new Sub1 { Id = 1, Desc = "Test" }, new Sub1 { Id = 2, Desc = "Test2" } } };
var serializeTest2 = new Test<Sub2> { TestId = 1, Name = "Test", Shipments = new List<Sub2> { new Sub2 { IdWhatever = 1, DescWhatever = "Test" }, new Sub2 { IdWhatever = 2, DescWhatever = "Test2" } } };
var serialized = serializeTest.SerializeToXml();
var serialized2 = serializeTest2.SerializeToXml();
var deserialized = serialized.DeserializeXml<Test<Sub1>>();
var deserialized2 = serialized2.DeserializeXml<Test<Sub2>>();
Console.WriteLine(serialized);
Console.WriteLine();
Console.WriteLine(serialized2);
Console.ReadLine();
}
}
And my Serialize and DeSerialize extension methods:
public static string SerializeToXml<T>(this T valueToSerialize, string namespaceUsed = null)
{
var ns = new XmlSerializerNamespaces(new XmlQualifiedName[] { new XmlQualifiedName(string.Empty, (namespaceUsed != null) ? namespaceUsed : string.Empty) });
using (var sw = new StringWriter())
{
using (XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { OmitXmlDeclaration = true }))
{
dynamic xmler = new XmlSerializer(typeof(T));
xmler.Serialize(writer, valueToSerialize, ns);
}
return sw.ToString();
}
}
public static T DeserializeXml<T>(this string xmlToDeserialize)
{
dynamic serializer = new XmlSerializer(typeof(T));
using (TextReader reader = new StringReader(xmlToDeserialize))
{
return (T)serializer.Deserialize(reader);
}
}
You don't need to specify the XmlElement name when the properties match the XML. A few solutions, some kinda hacky :).
HACKY: use regex string replace to replace <I#> and </I#> to
just <I> and </I>
SOMEWHAT HACKY: This might work for you:
How to deserialize an XML array containing multiple types of elements in C#,
but you'd have to add an attribute for i0, i1 ... i100, etc.
BEST: Is that your entire XML? I'd honestly just use LINQToXml and
do a Descendants("Path") and get an array of strings back with 1 line of code. Serialization is not really the best solution for this.

How do i add data from XML to list<>?

I try to read from an xml file, but its very clonky and a lot of the data I get is in bunch from a child. I get the Name, Age, and so on in one and therefor I can't add it to a list.
My xml-file looks like this:
<?xml version="1.0" encoding="UTF-8"?><People>
<Person>
<Age>30</Age>
<Name>Boy</Name>
<Sex>Male</Sex>
</Person>
<Person>
<Age>28</Age>
<Name>Girl</Name>
<Sex>Female</Sex>
</Person>
And in my xaml.cs file I have:
List<listTest> a = new List<listTest>();
var localFolder = ApplicationData.Current.LocalFolder;
XmlDocument xmlDocument;
var file = await localFolder.TryGetItemAsync("FoodData.xml") as IStorageFile;
xmlDocument = await XmlDocument.LoadFromFileAsync(file);
And with that I need to make a setup where I can take data from the XML and put it into list<> like this:
a.add(listTest {Name = "*DATA FROM XML*", Age ="*DATA FROM XML*", Sex="*DATA FROM XML*"});
I have tried to use LINQ and use p.NodeName == "xxx" to make searches, but I don't seem to get any data out.
Can some one show me how to get the data from my xml to a list?
Let's assume you have this class:
public class Person
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
Then, to load your XML file, you could do something like:
var doc = XDocument.Load("path to your file");
var people = doc.Root
.Descendants("person")
.Select(node => new Person
{
Name = node.Element("name").Value,
Sex = node.Element("sex").Value,
Age = int.Parse(node.Element("age").Value)
})
.ToList();
See https://msdn.microsoft.com/en-us/library/bb353813.aspx
Here is a simple example of an XML import. After this code executes, results will reflect if people were found (true or false), and msg will be a list of error messages (or empty if success).
var results = true;
var msg = new List<string>(0);
XDocument aDocument = null;
try
{
aDocument = XDocument.Load("");
}
catch (Exception e)
{
results = false;
msg.Add(string.Format("Unable to open file:{0}", ""));
msg.Add(e.Message);
}
if (aDocument != null)
{
var thePeople = aDocument.Descendants("Person").ToArray();
if (thePeople.Any())
{
// there were people in the file. People is an array of XML Nodes containing your person.
foreach (var pers in thePeople.Select(p => new Person().FromXML(p)))
{
// here is a person
}
}
else
{
results = false;
msg.Add("No people found.");
}
}
Hope this helps.
Addition.
You could do something like this in your Person Class. I've added code to the original to illustrate usage.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public XElement ToXML()
{
return new XElement("Person", "Name", Name,
new XElement("Age", Age),
new XElement("Sex", Sex));
}
public Person FromXML(XElement node)
{
try { Name = node.Element("Name").Value; }
catch { Name = "Not Found"; }
try { Age = Convert.ToInt16(node.Element("Age").Value); }
catch { Age = -1; }
try { Sex = node.Element("Sex").Value; }
catch { Sex = ""; }
return this;
}
}

XPath matches for a XML serialized DataContract class

I have a DataContract class MyDataContract. I am serializing it to a XML file. Later, at a totally different "place" in my application I am loading this file. There I want to verify to load only, if the content of the file matches special conditions. Let's say I store a person and the association to the person's vehicle assuming a person can own just one vehicle. :-)
Here the DataContract classes:
namespace Test.DataContracts
{
[DataContract]
public class MyDataContract
{
[DataMember]
public string Identifier { get; set; }
[DataMember]
public Common.Person Person { get; set; }
[DataMember]
public Common.Vehicle Vehicle { get; set; }
}
}
namespace Test.DataContracts.Common
{
[DataContract]
public class Person
{
[DataMember]
public Global.Gender Gender { get; set; }
[DataMember]
public string Info { get; set; }
[DataMember]
public string Name { get; set; }
}
[DataContract]
public class Vehicle
{
[DataMember]
public string Info { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public string Vendor { get; set; }
}
}
namespace Test.DataContracts.Global
{
[DataContract]
public class Gender
{
[DataMember]
public int Type { get; set; }
[DataMember]
public string Name { get; set; }
}
}
Results in the following serialized XML:
<?xml version="1.0" encoding="utf-8"?>
<MyDataContract xmlns="http://schemas.datacontract.org/2004/07/Test.DataContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Identifier>123456789</Identifier>
<Person xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
<a:Gender xmlns:b="http://schemas.datacontract.org/2004/07/Test.DataContracts.Global">
<b:Name>Female</b:Name>
<b:Type>0</b:Type>
</a:Gender>
<a:Info>She is a beautiful lady.</a:Info>
<a:Name>Jane Doe</a:Name>
</Person>
<Vehicle xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
<a:Info>A super great car.</a:Info>
<a:Name>Mustang 1983 Turbo GT</a:Name>
<a:Vendor>Ford</a:Vendor>
</Vehicle>
</MyDataContract>
Now I want to filter out only Female (Type = 0) persons that own any Ford (Vendor = Ford) vehicle. I tried the following, but it always results in false for the matches.
string path = #"c:\janedoe.xml";
var xmlDoc = new XmlDocument();
xmlDoc.Load(path);
XmlNodeList xNodes = xmlDoc.SelectNodes(#"//namespace::*[not(. = ../../namespace::*)]");
var xNamespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
foreach (XmlNode node in xNodes)
{
if (!string.IsNullOrWhiteSpace(xNamespaceManager.LookupNamespace(node.LocalName))) continue;
xNamespaceManager.AddNamespace(node.LocalName, node.Value);
}
using (var fs = new FileStream(path, FileMode.Open))
{
var xDocument = new XPathDocument(fs);
var xNavigator = xDocument.CreateNavigator();
XPathExpression exp1 = xNavigator.Compile(string.Format("MyDataContract/Person/Gender/Type/descendant::*[contains(text(), '{0}')]", "0"));
exp1.SetContext(xNamespaceManager);
XPathExpression exp2 = xNavigator.Compile(string.Format("MyDataContract/Vehicle/Vendor/descendant::*[contains(text(), '{0}')]", "Ford"));
exp2.SetContext(xNamespaceManager);
if (xNavigator.Matches(exp1) && xNavigator.Matches(exp2))
{
Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}
}
Can anyone help?
UPDATE 1 - I have changed the code using XmlNamespaceManager. But still results in false when executing xNavigator.Matches(exp1).
If you have XDocument it is easier to use LINQ-to-XML:
var xdoc = XDocument.Load(memorystream);
// Making it simple, grab the first
var type = xdoc.Descendants(XName.Get("Type","http://schemas.datacontract.org/2004/07/Test.DataContracts.Global")).FirstOrDefault();
var vendor = xdoc.Descendants(XName.Get("Vendor", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Common")).FirstOrDefault();
string path = "blah";
if (type != null && type.Value == "0" && vendor != null && vendor.Value == "Ford")
{
Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}
If you are sure that XPath is the only solution you need:
using System.Xml.XPath;
var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("a", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Global");
var name = document.XPathSelectElement("path", namespaceManager).Value;

Categories

Resources