How do we merge 2 XML records that have the same schema?
For example let's say we have two records such as :
<msg>
<name>alex</name>
<payload></payload>
</msg>
and the second:
<msg>
<name></name>
<payload>blabla</payload>
</msg>
The expected result:
<msg>
<name>alex</name>
<payload>blabla</payload>
</msg>
We simply took the non-empty value.
How do we merge two xml records with the same schema?
A simple implementation would just go though the elements pairing corresponding items by name and create a new element choosing the text of the non-blank element.
XElement ShallowMerge(XElement a, XElement b) =>
new XElement(a.Name,
from ae in a.Elements()
join be in b.Elements() on ae.Name equals be.Name
select new XElement(ae.Name,
!String.IsNullOrWhiteSpace((string)ae)
? (string)ae
: (string)be
)
);
//two lists for temp storing .xml data
List<Person> list1= new List<Person>();
List<Person> list2= new List<Person>();
List<Person> list3= new List<Person>();//for mixing
string path=#"c:\.....";
//properties of .xml notes like 'name' and 'payload'
public class Person
{
public string name{ get; set; }//1
public string payload{ get; set; }//2
}
//load both .xml files saperatly
void load_list1()
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(path + #"\list1.xml");
foreach (XmlNode xnode in xdoc.SelectNodes("Items/Item"))
{
Person p = new Person();
p.name= xnode.SelectSingleNode("a").InnerText;
p.payload= xnode.SelectSingleNode("b").InnerText;
list1.Items.Add(p);
}
}
void load_list2()
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(path + #"\list2.xml");
foreach (XmlNode xnode in xdoc.SelectNodes("Items/Item"))
{
Person p = new Person();
p.name= xnode.SelectSingleNode("a").InnerText;
p.payload= xnode.SelectSingleNode("b").InnerText;
list2.Items.Add(p);
}
}
//start mixing both lists
void mixdata()
{
for(int i=0;i<list1.Items.Count;i++)
{
Person p= new Person();
p.name=list1.Items[i].name;
p.payload=list2.Items[i].payload;
list3.Items.Add(p);
}
}
//saving final mixed list to .xml
void save()
{
XmlDocument xdoc = new XmlDocument();
xdoc.Load(path + #"\Mixed_data.xml");
XmlNode xnode = xdoc.SelectSingleNode("Items");
xnode.RemoveAll();
foreach (Person i in list3)
{
XmlNode xtop = xdoc.CreateElement("Item");
XmlNode x1 = xdoc.CreateElement("a");
XmlNode x2 = xdoc.CreateElement("b");
x1.InnerText = i.name;
x2.InnerText = i.payload;
xtop.AppendChild(x1);
xtop.AppendChild(x2);
xdoc.DocumentElement.AppendChild(xtop);
}
xdoc.Save(path + #"\data.xml");
}
//let me know any errors
Related
I'm trying to select nodes in c# within a XML file with below example.
<dsEq xmlns="http://tempuri.org/dsEq.xsd">
<Dago>
<EID>XX</EID>
below code is working :
private static List<string> getListOfEID(XmlDocument xmlDoc)
{
List<string> ListingEID = new List<string>();
XmlNodeList nodeCollection = xmlDoc.GetElementsByTagName("EID");
foreach (XmlNode elt in nodeCollection)
{
ListingEID.Add(elt.InnerText.ToString());
}
return ListingEID;
}
While I tried a lot of things, without success with Selectnodes method, not working :
private static List<string> getListOfEID(XmlDocument xmlDoc)
{
List<string> ListingEID = new List<string>();
XmlNodeList nodeCollection = xmlDoc.SelectNodes("/dsEq/Dago/EID");
foreach (XmlNode elt in nodeCollection)
{
ListingEID.Add(elt.InnerText.ToString());
}
return ListingEID;
}
Thanks in advance !
tried a lot of different xPath without success.
adding nameSpace seems not helping
private static List<string> getListOfEID(XmlDocument xmlDoc)
{
List<string> ListingEID = new List<string>();
XmlNamespaceManager nameManager = new XmlNamespaceManager(xmlDoc.NameTable);
nameManager.AddNamespace("myEID","/dsEq/Dago");
XmlNodeList nodeCollection = xmlDoc.SelectNodes("myEID");
foreach (XmlNode elt in nodeCollection)
{
ListingEID.Add(elt.InnerText.ToString());
}
return ListingEID;
}
==> Show nil node
It is very easy via LINQ to XML.
LINQ to XML is available in the .Net Framework since 2007.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<dsEq xmlns='http://tempuri.org/dsEq.xsd'>
<Dago>
<EID>XX</EID>
</Dago>
<Dago>
<EID>YY</EID>
</Dago>
</dsEq>");
XNamespace ns = xdoc.Root.GetDefaultNamespace();
List<String> listOfStrings = xdoc.Descendants(ns + "EID")
.Select(x => x.Value).ToList();
Console.Write(listOfStrings);
}
I too think it is super simple with Linq to XML as #Yitzhak Khabinsky showed already. Yours is not working because you are not using Xpath correctly.
void Main()
{
var doc = new XmlDocument();
doc.LoadXml(sample);
var eids = getListOfEID(doc);
foreach (var eid in eids)
{
Console.WriteLine(eid);
}
}
private static List<string> getListOfEID(XmlDocument xmlDoc)
{
List<string> ListingEID = new List<string>();
XmlNamespaceManager nameManager = new XmlNamespaceManager(xmlDoc.NameTable);
nameManager.AddNamespace("x","http://tempuri.org/dsEq.xsd");
XmlNodeList nodeCollection = xmlDoc.SelectNodes("//x:EID", nameManager);
foreach (XmlNode elt in nodeCollection)
{
ListingEID.Add(elt.InnerText.ToString());
}
return ListingEID;
}
static readonly string sample = #"<dsEq xmlns=""http://tempuri.org/dsEq.xsd"">
<Dago>
<EID>XX</EID>
</Dago>
<Dago>
<EID>YY</EID>
</Dago>
<Dago>
<EID>ZZ</EID>
</Dago>
</dsEq>";
I'm complete rookie I need to retrieve the value of last id in XML file here is
You can also use XPath within the Xml DOM like this :
string title;
XmlDocument xml = new XmlDocument();
xml.Load("~/purchases.xml");
// Or any other method to load your xml data in the XmlDocument.
// For example if your xml data are in a string, use the LoadXml method.
XmlElement elt = xml.SelectSingleNode("//SubMenu[#id='1']") as XmlElement;
if(elt!=null)
{
name=elt.GetAttribute("title");
}
Reference
As Jon suggested, you can use Linq To XML here.
XElement books = XElement.Load(filePath);
var lastId = books.Descendants("book").Select(x=>Int32.Parse(x.Attribute("ID").Value)).Last();
This will give you the last ID in the current list.You can now create your new Node
books.Add(new XElement("book",new XAttribute("ID",(lastId+1).ToString()),
new XAttribute("title","New Title"),
new XAttribute("price","1234")));
books.Save(filePath);
XmlDocument doc = new XmlDocument();
doc.Load("Yourxmlfilepath.xml");
//Display all the book titles.
XmlNodeList xNodeList = doc.SelectNodes("/bookstore/book");
foreach (XmlNode xNode in xNodeList)
{
var employeeName = xNode.OuterXml;
XmlDocument docnew = new XmlDocument();
docnew.LoadXml(employeeName);
foreach (XmlElement report in docnew.SelectNodes("book"))
{
string ID = report.GetAttribute("ID");
string title = report.GetAttribute("title");
string quantity = report.GetAttribute("quantity");
string price = report.GetAttribute("price");
}
}
I want to create nested xml so that the result would look like:
<?xml version=\"1.0\" encoding=\"UTF-8\"?><TASKLOADLOG>
<PERSON>
<EMAIL>data</EMAIL><LOADED>OK</LOADED><LOADERROR>ABC</LOADERROR>
</PERSON>
<PERSON>
<EMAIL>data</EMAIL><LOADED>OK</LOADED><LOADERROR>ABC</LOADERROR>
</PERSON>
<PERSON>
<EMAIL>data</EMAIL><LOADED>OK</LOADED><LOADERROR>ABC</LOADERROR>
</PERSON>
</TASKLOADLOG>"
I wrote following code and It crashed in a loop
XmlDocument XmlResponse = new XmlDocument();
XmlDeclaration xDeclare = XmlResponse.CreateXmlDeclaration("1.0", "UTF-8", null);
XmlElement documentRoot = XmlResponse.DocumentElement;
XmlResponse.InsertBefore(xDeclare, documentRoot);
XmlElement el = (XmlElement)XmlResponse.AppendChild(XmlResponse.CreateElement("TASKLOADLOG"));
List<XmlElement> ls = new List<XmlElement>();
for (int i = 0; i < 3; i++)
{
ls[i].AppendChild(XmlResponse.CreateElement("EMAIL")).InnerText = "data";
ls[i].AppendChild(XmlResponse.CreateElement("LOADED")).InnerText = "OK";
ls[i].AppendChild(XmlResponse.CreateElement("LOADERROR")).InnerText = "ABC";
}
MessageBox.Show(XmlResponse.OuterXml);
I don't now how to define PERSON what I need to write to fix my code?
The problem is simple, you are not creating the PERSON node. Email, Loaded and LoadError should be child nodes to Person node.
Edit:
just to let you know, you could even use class Serialization for the XML you are trying to generate.
For Example:
[Serializable]
public class Person
{
[XmlAttribute]
public string Email { get; set; }
public string Loaded { get; set; }
public string LoadError{ get; set; }
}
Person p = new Person
{
Email = "abc",
Loaded = "abc";
LoadError = "abc"
};
new XmlSerializer(typeof(Person)).Serialize(Console.Out, Person);
thanks about the quick answer
I wrote following code and It got error in XmlResponse.Root.AppendChild :
XmlDocument XmlResponse = new XmlDocument();
XmlDeclaration xDeclare = XmlResponse.CreateXmlDeclaration("1.0", "UTF-8", null);
XmlElement documentRoot = XmlResponse.DocumentElement;
XmlResponse.InsertBefore(xDeclare, documentRoot);
XmlElement el = (XmlElement)XmlResponse.AppendChild(XmlResponse.CreateElement("TASKLOADLOG"));
//List<XmlElement> ls = new List<XmlElement>();
for (int i = 0; i < 3; i++)
{
XmlResponse.Root.AppendChild(new XElement("PERSON",
new XElement("EMAIL", "data"),
new XElement("LOADED", "OK"),
new XElement("LOADERROR", "ABC")
)));
}
MessageBox.Show(XmlResponse.OuterXml);
Your code is crashing because you are referencing the first index of your list ls when the list is empty.
Create your document like this:
var doc = XDocument(new XElement("TASKLOADLOG"));
for (int i = 0; i < 3; i++)
doc.Root.AppendChild(new XElement("PERSON",
new XElement("EMAIL", "data"),
new XElement("LOADED", "OK"),
new XElement("LOADERROR", "ABC")
)));
}
I'm trying to write a list of objects to valid xml. My code looks basicly like this:
public class Person
{
public string First { get; set; }
public string Last { get; set; }
}
List<Person> people = new List<Person>();
XElement elements = new XElement("People",
from p in people
select new XElement("Person",
new XElement("First", p.First),
new XElement("Last", p.Last)));
string output = elements.ToString();
Unfortunately output when written to a file looks like this:
<People>
<Person>
<First>Tom</First>
<Last>Hanks</Last>
</Person>
</People>
XDeclaration declaration = new XDeclaration("1.0", "utf-8", "yes");
XProcessingInstruction procInstruction = new XProcessingInstruction("xml-stylesheet", "type='text/xsl'");
XElement root = new XElement("Store");
XDocument settingsFile = new XDocument(declaration, procInstruction, root);
foreach (string key in persistentSettings.Keys)
{
string value = persistentSettings[key];
if (!string.IsNullOrEmpty(value))
{
XElement setting = new XElement("Setting", new XAttribute("Name", key));
setting.Value = value;
root.Add(setting);
}
}
settingsFile.Save(SettingsFileName);
What is that I'm doing wrong here?
I had to use the parse function in the Constructor of xElement
XElement setting
= new XElement("Setting", new XAttribute("Name", key), XElement.Parse(value));
instead of
XElement setting = new XElement("Setting", new XAttribute("Name", key));
setting.Value = value;
I'm new to using Linq and XMLDocument.
I have a simple XML file and I want to loop through all of the elements and print the tag and value. I don't want to use the XML Tags when looping through. this is what I have so far.
XML file:
<?xml version="1.0" encoding="UTF-8"?>
<Step1>One
<Step2>Two
<Step3>Three
<Step4>Four
</Step4>
</Step3>
</Step2>
</Step1>
C# Code
private void StartIt()
{
System.Xml.XmlDocument xd = new System.Xml.XmlDocument();
xd.Load(#"C:\Projects\GetXML\testLayers.xml");
XmlNodeList nl = xd.SelectNodes("Layer1");
foreach (XmlNode xnode in nl)
{
Console.WriteLine(xnode.Name + " = " + xnode.InnerText); // + " " + xnode.InnerXml);
}
}
Results:
Step1 = One
Two
Three
Four
What I want:
Step1 = One
Step2 = Two
Step3 = Three
Step4 = Four
Any suggestions?
With a little help of Linq,
XmlDocument doc = new XmlDocument();
doc.Load(fname);
var nodes = doc.SelectNodes("//*[text()]")
.Cast<XmlNode>()
.Select(n => new {
Name= n.Name,
Value = n.SelectSingleNode("text()").Value
})
.ToList();
// System.Xml.XmlDocument version
XmlDocument xd = new XmlDocument();
xd.Load(#"C:\Projects\GetXML\testLayers.xml");
foreach (XmlElement step in xd.SelectNodes("//*"))
{
Console.WriteLine("{0} = {1}", step.Name,
step.SelectSingleNode("text()").Value);
}
// System.Xml.Linq.XDocument version
XDocument xdLinq = XDocument.Load(#"C:\Projects\GetXML\testLayers.xml");
foreach (XElement step in xdLinq.XPathSelectElements("//*"))
{
Console.WriteLine("{0} = {1}", step.Name,
step.Nodes().Where(n => n.NodeType == XmlNodeType.Text).FirstOrDefault());
}
You can do the same using LINQ to XML and XDocument class:
var xDoc = XDocument.Load("Input.txt");
foreach (var e in xDoc.Descendants())
{
Console.WriteLine("{0} = {1}", e.Name, e.Nodes().OfType<XText>().First().Value.Trim());
}