Doing CRUD on XML using id attributes in C# ASP.NET - c#

I'm a LAMP guy and ended up working this small news module for an asp.net site, which I am having some difficulty with. I basically am adding and deleting elements via AJAX based on the id. Before, I had it working based on the the index of a set of elements, but would have issues deleting, since the index would change in the xml file and not on the page (since I am using ajax).
Here is the rundown
news.xml
<?xml version="1.0" encoding="utf-8"?>
<news>
<article id="1">
<title>Red Shield Environmental implements the PARCSuite system</title>
<story>Add stuff here</story>
</article>
<article id="2">
<title>Catalyst Paper selects PARCSuite for its Mill-Wide Process...</title>
<story>Add stuff here</story>
</article>
<article id="3">
<title>Weyerhaeuser uses Capstone Technology to provide Control...</title>
<story>Add stuff here</story>
</article>
</news>
Page sending del request:
<script type="text/javascript">
$(document).ready(function () {
$('.del').click(function () {
var obj = $(this);
var id = obj.attr('rel');
$.post('add-news-item.aspx',
{ id: id },
function () {
obj.parent().next().remove();
obj.parent().remove();
}
);
});
});
</script>
<a class="del" rel="1">...</a>
<a class="del" rel="1">...</a>
<a class="del" rel="1">...</a>
My functions
protected void addEntry(string title, string story)
{
XmlDocument news = new XmlDocument();
news.Load(Server.MapPath("../news.xml"));
XmlAttributeCollection ids = news.Attributes;
//Create a new node
XmlElement newelement = news.CreateElement("article");
XmlElement xmlTitle = news.CreateElement("title");
XmlElement xmlStory = news.CreateElement("story");
XmlAttribute id = ids[0];
int myId = int.Parse(id.Value + 1);
id.Value = ""+myId;
newelement.SetAttributeNode(id);
xmlTitle.InnerText = this.TitleBox.Text.Trim();
xmlStory.InnerText = this.StoryBox.Text.Trim();
newelement.AppendChild(xmlTitle);
newelement.AppendChild(xmlStory);
news.DocumentElement.AppendChild(newelement);
news.Save(Server.MapPath("../news.xml"));
}
protected void deleteEntry(int selectIndex)
{
XmlDocument news = new XmlDocument();
news.Load(Server.MapPath("../news.xml"));
XmlNode xmlnode = news.DocumentElement.ChildNodes.Item(selectIndex);
xmlnode.ParentNode.RemoveChild(xmlnode);
news.Save(Server.MapPath("../news.xml"));
}
I haven't updated deleteEntry() and you can see, I was using the array index but need to delete the article element based on the article id being passed. And when adding an entry, I need to set the id to the last elements id + 1. Yes, I know SQL would be 100 times easier, but I don't have access so... help?

Linq to XML should make this a lot simpler. Here would be the equivalent of what you are trying:
public void AddEntry(string title, string story)
{
var newElement = new XElement("article", new XElement("title", title), new XElement("story", story));
XDocument doc = XDocument.Parse(testXml);
var maxId = doc.Descendants("article").Attributes("id").Max(x => int.Parse(x.Value));
newElement.Add(new XAttribute("id", ++maxId));
doc.Descendants("news").First().Add(newElement);
//save the document
}
public void DeleteEntry(int selectIndex)
{
XDocument doc = XDocument.Parse(testXml);
doc.Descendants("article").Where(x => int.Parse(x.Attribute("id").Value) == selectIndex).Remove();
//save the document
}
Based on the size of your xml file and the number of requests, you may want to look into other approaches than loading the document, and saving it for each call to add and delete.
EDIT: Note that you would need to add null checks to the above code...

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
namespace LinqToXML
{
class Program
{
static void Main(string[] args)
{
int ch;
do
{
Console.WriteLine("Enter the operation you want to execute");
Console.WriteLine("Press 1 to show previous record");
Console.WriteLine("Press 2 to add new record");
Console.WriteLine("Press 3 to delete record");
Console.WriteLine("Press 4 to update record");
Record RecordObject = new Record();
int UserChoice = Convert.ToInt32(Console.ReadLine());
switch (UserChoice)
{
case 1:
RecordObject.ShowRecord();
break;
case 2:
RecordObject.AddRecord();
break;
case 3:
RecordObject.DeleteRecord();
break;
case 4:
RecordObject.UpdateRecord();
break;
default:
Console.WriteLine("Invalid Option");
break;
}
Console.WriteLine("\tDo you Want to CONTINUE?\n\t1.YES\n\t2.NO");
ch = Convert.ToInt32(Console.ReadLine());
} while (ch == 1);
}
}
class Info
{
public string StudentName { get; set; }
public int StudentId { get; set; }
public int StudentAge { get; set; }
public string StudentCity { get; set; }
}
class Record
{
string fileAddress = #"C:\XML.xml";
XmlDocument doc = new XmlDocument();
public void ShowRecord()
{
if (File.Exists(fileAddress))
{
string line;
using (StreamReader sr = new StreamReader(fileAddress))
{
while ((line = sr.ReadLine()) != null)
{
Console.WriteLine(line);
}
Console.ReadLine();
}
}
else
{
Console.WriteLine("No record exist");
}
}
public void AddRecord()
{
Console.WriteLine("Enter Student Name :");
string StuName = Console.ReadLine();
Console.WriteLine("Enter Student Age :");
int StuAge = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter Student City :");
string StuCity = Console.ReadLine();
Info InfoObj = new Info();
InfoObj.StudentName = StuName;
InfoObj.StudentAge = StuAge;
InfoObj.StudentCity = StuCity;
FileStream FileStreamObj = null;
if (File.Exists(fileAddress))
{
FileStreamObj = new FileStream(fileAddress, FileMode.Open, FileAccess.ReadWrite);
FileStreamObj.Close();
XmlDocument doc = new XmlDocument();
doc.Load(fileAddress);
XmlNodeList nodes = doc.SelectNodes("//students/student");
int nodeCount = nodes.Count;
nodeCount++;
XmlNodeList students = doc.SelectNodes("//students");
foreach (XmlNode student in students)
{
XmlNode parentNode = doc.CreateElement("student");
XmlAttribute attribute = doc.CreateAttribute("id");
attribute.Value = nodeCount.ToString();
parentNode.Attributes.Append(attribute);
student.AppendChild(parentNode);
XmlNode studentName = doc.CreateElement("studentName");
studentName.InnerText = StuName;
parentNode.AppendChild(studentName);
XmlNode studentAge = doc.CreateElement("studentAge");
studentAge.InnerText = StuAge.ToString();
parentNode.AppendChild(studentAge);
XmlNode studentCity = doc.CreateElement("studentCity");
studentCity.InnerText = StuCity;
parentNode.AppendChild(studentCity);
doc.Save(fileAddress);
}
}
else
{
FileStreamObj = new FileStream(fileAddress, FileMode.Create, FileAccess.ReadWrite);
FileStreamObj.Close();
int StudentId = 1;
XmlDocument doc = new XmlDocument();
XmlNode rootNode = doc.CreateElement("students");
doc.AppendChild(rootNode);
XmlNode parentNode = doc.CreateElement("student");
XmlAttribute attribute = doc.CreateAttribute("id");
attribute.Value = StudentId.ToString();
parentNode.Attributes.Append(attribute);
rootNode.AppendChild(parentNode);
XmlNode studentName = doc.CreateElement("studentName");
studentName.InnerText = StuName;
parentNode.AppendChild(studentName);
XmlNode studentAge = doc.CreateElement("studentAge");
studentAge.InnerText = StuAge.ToString();
parentNode.AppendChild(studentAge);
XmlNode studentCity = doc.CreateElement("studentCity");
studentCity.InnerText = StuCity;
parentNode.AppendChild(studentCity);
doc.Save(fileAddress);
}
}
public void UpdateRecord()
{
doc.Load(fileAddress);
Console.WriteLine("Enter ID of the record you want to update");
int InputChoice = Convert.ToInt32(Console.ReadLine());
Info infoObj = new Info();
XmlElement element = doc.DocumentElement;
XmlNode nodeElement = element.SelectSingleNode("student[#id='" + InputChoice + "']");
if (nodeElement == null)
{
Console.WriteLine("Record doesn't exist");
}
else
{
string oldName = nodeElement.ChildNodes[0].InnerText;
string oldAge = nodeElement.ChildNodes[1].InnerText;
string oldCity = nodeElement.ChildNodes[2].InnerText;
infoObj.StudentName = oldName;
infoObj.StudentAge = Convert.ToInt32(oldAge);
infoObj.StudentCity = oldCity;
Console.WriteLine("Old Values are:\n\tName: " + infoObj.StudentName + "\n\tAge" + infoObj.StudentAge + " \n\tCity" + infoObj.StudentCity + "");
Console.WriteLine("Enter new name");
string newName = Console.ReadLine();
Console.WriteLine("Enter new Age");
int newAge = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Enter new city");
string newCity = Console.ReadLine();
infoObj.StudentName = newName;
infoObj.StudentAge = newAge;
infoObj.StudentCity = newCity;
nodeElement.ChildNodes[0].InnerText = infoObj.StudentName;
nodeElement.ChildNodes[1].InnerText = infoObj.StudentAge.ToString();
nodeElement.ChildNodes[2].InnerText = infoObj.StudentCity;
doc.Save(fileAddress);
}
}
public void DeleteRecord()
{
doc.Load(fileAddress);
Console.WriteLine("Enter the Id you want to delete");
string inputValue = Console.ReadLine();
XmlElement element = doc.DocumentElement;
XmlNode nodeElement = element.SelectSingleNode("student[#id='" + inputValue + "']");
if (nodeElement == null)
{
Console.WriteLine("Record doesn't exist");
}
else
{
element.RemoveChild(nodeElement);
doc.Save(fileAddress);
Console.WriteLine("Sucessfully deleted");
}
}
}
}

Related

How to Read XML element with namespace value

I am facing problem in fetching xml element value if it is having name space. please help me what is the problem here. My xml string is below
<PurchaseOrder xmlns:aw="http://www.adventure-works.com"> <aw:ShippingAddress> <aw:Name>John</aw:Name> <aw:Street>123 Main St.</aw:Street> <aw:City>Seattle</aw:City> <aw:State>WA</aw:State> <aw:Zip>98113</aw:Zip> <aw:Country>USA</aw:Country> </aw:ShippingAddress> <aw:ShippingAddress> <aw:Name>Chris Preston</aw:Name> <aw:Street>123 Robin St.</aw:Street> <aw:City>Newyork</aw:City> <aw:State>TU</aw:State> <aw:Zip>98113</aw:Zip> <aw:Country>USA</aw:Country> </aw:ShippingAddress> <aw:ShippingAddress> <aw:Name>Charlis</aw:Name> <aw:Street>53 Jacob St.</aw:Street> <aw:City>California</aw:City> <aw:State>DOWNTOWN</aw:State> <aw:Zip>98111</aw:Zip> <aw:Country>USA</aw:Country> </aw:ShippingAddress> </aw:PurchaseOrder>
my code is below
XDocument doc = XDocument.Load("PurchaseOrder.xml");
List<PurchaseOrder> listWO = new List<PurchaseOrder>();
foreach (XElement el in doc.Root.Elements())
{
if ( el.Elements().Count() > 0)
{
PurchaseOrder po = new PurchaseOrder
{
Name = el.Elements("aw:Name").First().Value,
City = el.Elements("aw:City").First().Value,
Country = el.Elements("aw:Country").First().Value
};
listPO.Add(po):
}
}
Here i am not getting the value of each "ShippingAddress" wise.
See changes below :
XDocument doc = XDocument.Load("PurchaseOrder.xml");
XNamespace awNs = doc.Root.GetNamespaceOfPrefix("aw");
List<PurchaseOrder> listWO = new List<PurchaseOrder>();
foreach (XElement el in doc.Root.Elements())
{
if ( el.Elements().Count() > 0)
{
PurchaseOrder po = new PurchaseOrder
{
Name = el.Elements(awNs + "Name").First().Value,
City = el.Elements(awNs + "City").First().Value,
Country = el.Elements(awNs + "Country").First().Value
};
listPO.Add(po):
}
}

XML to String List

I have some code that I need to put into a string list in C# and I am reading this code from an XML files and the layout of it is something like below...
<?xml version="1.0"?>
<accountlist>
<main>
<account id="1" special_id="4923959">
<username>Adam</username>
<motto>Hello Everyone>
<money>1004</money>
<friends>394</friends>
<rareid>9</rareid>
<mission>10</mission>
</account>
</main>
</accountlist>
How can I put each account tag into a string list? from the first < account > to the < / account > tag?
Please do NOT tell me to go to the link below as it does NOT work!!
How to read a XML file and write into List<>?
So far I have tried the below code, and the string list just stays empty
XDocument doc = XDocument.Parse(this._accountsFile);
List<string> list = doc.Root.Elements("account")
.Select(element => element.Value)
.ToList();
this._accounts = list;
You'll have to use Descendants instead of Elements:
List<string> list = doc.Root.Descendants("account").Descendants()
.Select(element => element.Value)
.ToList();
Elements only returns child elements of the element (in case of the root element this means <main>).
Descendants returns the entire tree inside the element.
Also: You'll have to fix the tag <motto>Hello Everyone> to <motto>Hello Everyone</motto>
This will work on your example (but you need to close this tag <motto>Hello Everyone>
public List<string> GetAccountsAsXmlList(string filePath)
{
XmlDocument x = new XmlDocument();
x.Load(filePath);
List<string> result = new List<string>();
XmlNode currentNode;
foreach (var accountNode in x.LastChild.FirstChild.ChildNodes)
{
currentNode = accountNode as XmlNode;
result.Add(currentNode.InnerXml);
}
return result;
}
EDIT as an answer to your question:
Is there a way I can get the id and specal_id in a seperate string?
you can use currentNode.Attributes["YourAttributeName"].Value, to get the values.
assume you have class Account :
class Account
{
public string accountXml { get; set; }
public string Id { get; set; }
public string Special_id { get; set; }
}
Then :
public List<Account> GetAccountsAsXmlList(string filePath)
{
XmlDocument x = new XmlDocument();
x.Load(filePath);
List<Account> result = new List<Account>();
XmlNode currentNode;
foreach (var accountNode in x.LastChild.FirstChild.ChildNodes)
{
currentNode = accountNode as XmlNode;
result.Add(new Account
{
accountXml = currentNode.InnerXml,
Id = currentNode.Attributes["id"].Value,
Special_id = currentNode.Attributes["special_id"].Value,
});
}
return result;
}
Use XPath to get the account element first:
using System.Xml.XPath;
XDocument doc = XDocument.Parse(xml);
foreach(var account in doc.XPathSelectElements("accountlist/main/account")){
List<string> list = account.Descendants()
.Select(element => element.Value)
.ToList();
}
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication37
{
class Program
{
static void Main(string[] args)
{
string input =
"<?xml version=\"1.0\"?>" +
"<accountlist>" +
"<main>" +
"<account id=\"1\" special_id=\"4923959\">" +
"<username>Adam</username>" +
"<motto>" +
"Hello Everyone>" +
"<money>1004</money>" +
"<friends>394</friends>" +
"<rareid>9</rareid>" +
"<mission>10</mission>" +
"</motto>" +
"</account>" +
"</main>" +
"</accountlist>";
XDocument doc = XDocument.Parse(input);
var results = doc.Descendants("accountlist").Select(x => new {
id = x.Element("main").Element("account").Attribute("id").Value,
special_id = x.Element("main").Element("account").Attribute("special_id").Value,
username = x.Element("main").Element("account").Element("username").Value,
motto = x.Element("main").Element("account").Element("motto").FirstNode.ToString(),
money = x.Element("main").Element("account").Element("motto").Element("money").Value,
friends = x.Element("main").Element("account").Element("motto").Element("friends").Value,
rareid = x.Element("main").Element("account").Element("motto").Element("rareid").Value,
mission = x.Element("main").Element("account").Element("motto").Element("mission").Value,
}).ToList();
}
}
}

How can I avoid NullReferenceExceptions when nodes don't exist in my XML files?

I have the following code:
static void Main(string[] args)
{
XmlDocument xml = new XmlDocument();
xml.Load(#"C:\MR.xml");
XmlNodeList stations = xml.SelectNodes("//FileDump/Message/Attachment");
var Message_ID = xml.SelectSingleNode("//FileDump/Message/MsgID").InnerXml;
Console.WriteLine("Message ID is :{0}", Message_ID);
foreach (XmlNode station in stations)
{
var File_Name = station.SelectSingleNode("FileName").InnerXml;
var File_ID = station.SelectSingleNode("FileID").InnerXml;
}
}
FileID and FileName do not always exist in some files. How can I avoid NullReferenceExceptions in this case?
I would try to something like this if that check has to happen in lot of places and to keep the code simple and clear
public static class Helpers
{
public static string GetInnerXml(this XmlNode node, string innerNodeName)
{
string innerXml = "";
XmlNode innerNode = node.SelectSingleNode(innerNodeName);
if (innerNode != null)
{
innerXml = innerNode.InnerXml;
}
return innerXml;
}
}
and use it like this
static void Main(string[] args)
{
XmlDocument xml = new XmlDocument();
xml.Load(#"C:\MR.xml");
XmlNodeList stations = xml.SelectNodes("//FileDump/Message/Attachment");
var Message_ID = xml.GetInnerXml("//FileDump/Message/MsgID");
Console.WriteLine("Message ID is :{0}", Message_ID);
foreach (XmlNode station in stations)
{
var File_Name = station.GetInnerXml("FileName");
var File_ID = station.GetInnerXml("FileID");
}
}
You could do something like:
string FileName= "";
string File_ID = "";
if (station.SelectSingleNode("FileName") != null)
File_Name = station.SelectSingleNode("FileName").InnerXml;
if (station.SelectSingleNode("FileID") != null)
File_ID = station.SelectSingleNode("FileID").InnerXml;
And continue processing if the vars are not the empty string ... ("") ...
static void Main(string[] args)
{
XmlDocument xml = new XmlDocument();
xml.Load(#"C:\MR.xml");
XmlNodeList stations = xml.SelectNodes("//FileDump/Message/Attachment");
var Message_ID = xml.SelectSingleNode("//FileDump/Message/MsgID").InnerXml;
Console.WriteLine("Message ID is :{0}", Message_ID);
foreach (XmlNode station in stations)
{
var fileNameNode = station.SelectSingleNode("FileName");
var fileIdNode = station.SelectSingleNode("FileID");
var File_Name = fileNameNode == null ? (string)null : fileNameNode.InnerXml;
var File_ID = fileIdNode == null ? (string)null : fileIdNode.InnerXml;;
}
}
I usually use extension methods for handling unexpected nulls.
public static string GetValueIfNotNull(this XmlAttribute xmlAttribute)
{
if (xmlAttribute == null)
{
return null;
}
return xmlAttribute.Value;
}
Then I can do myElement.Attribute("someAttribute").GetValueIfNotNull();

I want to recursively list the nodes in xml

I recursively want to display xml nodes. But unfortunately it doesn't work. The output is only the first element of the xml file. Why?
public string GetOutline(int indentLevel, XmlNode xnod)
{
StringBuilder result = new StringBuilder();
XmlNode xnodWorking;
result = result.AppendLine(new string('-', indentLevel * 2) + xnod.Name);
if (xnod.NodeType == XmlNodeType.Element)
{
if (xnod.HasChildNodes)
{
xnodWorking = xnod.FirstChild;
while (xnodWorking != null)
{
GetOutline(indentLevel + 1, xnodWorking);
xnodWorking = xnodWorking.NextSibling;
}
}
}
return result.ToString();
}
Here the code calling the function. The XML file begins with <Videos> then <Video>... etc...
private void button2_Click(object sender, EventArgs e)
{
SaveFileDialog fDialog = new SaveFileDialog();
fDialog.Title = "Save XML File";
fDialog.FileName = "drzewo.xml";
fDialog.CheckFileExists = false;
fDialog.InitialDirectory = #"C:\Users\Piotrek\Desktop";
if (fDialog.ShowDialog() == DialogResult.OK)
{
using (var newXmlFile = File.Create(fDialog.FileName));
{
string xmlTree = fDialog.FileName.ToString();
XmlDocument xdoc = new XmlDocument();
xdoc.Load(XML);
XmlNode xnodDE = xdoc.DocumentElement;
textBox2.Text = GetOutline(0, xnodDE);
//StringBuilder result = new StringBuilder();
/*
foreach (var childelement in xdoc.DescendantNodes().OfType<XElement>()
.Select(x => x.Name).Distinct())
{
result.Append(childelement + Environment.NewLine );
}
textBox2.Text = result.ToString();
*/
using (StreamWriter sw = File.AppendText(xmlTree))
{
sw.Write(textBox2.Text);
}
}
}
XML content :
<Videos>
<Video>
<Title>The Distinguished Gentleman</Title>
<Director>Jonathan Lynn</Director>
<Actors>
<Actor>Eddie Murphy</Actor>
<Actor>Lane Smith</Actor>
<Actor>Sheryl Lee Ralph</Actor>
<Actor>Joe Don Baker</Actor>
</Actors>
<Length>112 Minutes</Length>
<Format>DVD</Format>
<Rating>R</Rating>
</Video>
<Video>
<Title>Her Alibi</Title>
<Director>Bruce Beresford</Director>
<Length>94 Mins</Length>
<Format>DVD</Format>
<Rating>PG-13</Rating>
</Video>
</Videos>
You need to read all document line by line whith a for each or a while instruction
XmlReader reader = XmlReader.Create(your xml file);
reader.MoveToContent();
while (reader.Read())
{
// your code
}
reader.Close();
not the best way, try to have a look also on linq to xml
try that
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace testStackOverflow
{
class Program
{
static void Main(string[] args)
{
//Load xml
XDocument xdoc = XDocument.Load("test.xml");
//Run query
var lv1s = from lv1 in xdoc.Descendants("Video")
select new
{
title = lv1.Element("Title").Value
};
//Loop through results
foreach (var lv1 in lv1s)
{
Console.WriteLine(lv1.title);
}
Console.ReadLine();
}
}
}
You're not doing anything to add the results of the recursive calls to the string you're building. You need to do this:
result.Append(GetOutline(indentLevel + 1, xnodWorking));
And this modification should avoid the text nodes and nodes with the same name:
public string GetOutline(int indentLevel, XmlNode xnod)
{
StringBuilder result = new StringBuilder();
XmlNode xnodWorking;
result = result.AppendLine(new string('-', indentLevel * 2) + xnod.Name);
if (xnod.HasChildNodes)
{
List<string> foundElements = new List<string>();
xnodWorking = xnod.FirstChild;
while (xnodWorking != null)
{
if(xnodworking.NodeType == XmlNodeType.Element && !foundElements.Contains(xnodworking.Name))
{
result.Append(GetOutline(indentLevel + 1, xnodWorking));
foundElements.Add(xnodworking.Name);
}
xnodWorking = xnodWorking.NextSibling;
}
}
return result.ToString();
}

Construct a XML from a list of strings representing nested properties

I am trying to construct an XML, representing objects, but with ONLY the properties which have been modified between 2 objects, and the values in the second object. I don't know the structure of the classes, but I know that I will always have 2 objects of the same class.
Example :
public class A
{
public B Property_A_B { get; set; }
public C Property_A_C { get; set; }
}
public class B
{
public int Property_B_Int { get; set; }
public string Property_B_String { get; set; }
}
public class C
{
public bool Property_C_Bool { get; set; }
public D Property_C_D { get; set; }
}
public class D
{
public double Property_D_Double { get; set; }
}
I have 2 objects of type A. If the properties Property_B_Int, Property_B_String and Property_D_Double are not the same between my 2 objects, I have a string list which contains :
"A.Property_A_B.Property_B_Int"
"A.Property_A_B.Property_B_String"
"A.Property_A_C.Property_C_D.Property_D_Double"
Using these 3 strings, I have to construct that XML :
<A>
<Property_A_B>
<Property_B_Int>12345</Property_B_Int>
<Property_B_String>Hello world</Property_B_String>
</Property_A_B>
<Property_A_C>
<Property_C_D>
<Property_D_Double>456.76</Property_D_Double>
</Property_C_D>
</Property_A_C>
</A>
The objects can have many different structures and dephts. I only know their types and the names of the properties which are different That function must work for any object.
I wrote that code :
XmlDocument xml = new XmlDocument();
using (MemoryStream ms = new MemoryStream()) {
using (XmlWriter writer = XmlWriter.Create(ms)) {
// Début du fichier
writer.WriteStartDocument();
// Début de l'objet
writer.WriteStartElement(Objet_Fin.GetType().Name);
// Ecriture des champs modifiés
foreach (Difference diff in Differences) {
string[] composants_diff = diff.PropertyName.Split({ "." }, StringSplitOptions.RemoveEmptyEntries);
object sous_objet = Objet_Fin;
Type type_sous_objet = null;
PropertyInfo sous_propriete = default(PropertyInfo);
foreach (string composant_diff in composants_diff) {
// Pour chaque itération, on navigue vers la propriété suivante
type_sous_objet = sous_objet.GetType();
sous_propriete = type_sous_objet.GetProperty(composant_diff);
sous_objet = sous_propriete.GetValue(sous_objet);
// On ouvre un noeud XML pour chaque propriété passée
writer.WriteStartElement(composant_diff);
}
writer.WriteValue(sous_objet.ToString());
foreach (string composant_diff in composants_diff) {
// On ferme chaque noeud ouvert
writer.WriteEndElement();
}
}
// Fin de l'objet
writer.WriteEndElement();
// Fin du fichier
writer.WriteEndDocument();
// Ecriture dans le flux
writer.Flush();
}
// Ecriture du contenu du flux dans le XmlDocument
ms.Position = 0;
xml.Load(ms);
}
It almost works, but it generates the same object properties multiple times, instead of one. Something like that :
<A>
<Property_A_B>
<Property_B_Int>12345</Property_B_Int>
</Property_A_B>
<Property_A_B>
<Property_B_String>Hello world</Property_B_String>
</Property_A_B>
<Property_A_C>
<Property_C_D>
<Property_D_Double>456.76</Property_D_Double>
</Property_C_D>
</Property_A_C>
</A>
I don't know how to generate correctly the XML with every object properties written only once. Please could you help me ? Thanks.
The only correct way of doing this is using recursion. I used StringReader() for testing and it can easily be modified to StreamReader(). I added some real tough test data to validate the code.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string input1 = "a";
List<XElement> results1 = ProcessData(input1);
string input2 =
"A.Property_A_B.Property_B_Int\n" +
"A.Property_A_B.Property_B_String\n" +
"A.Property_A_C.Property_C_D.Property_D_Double";
List<XElement> results2 = ProcessData(input2);
string input3 =
"a.a.a.a.a.a.a\n" +
"a.a.a.a.a.a.b\n" +
"a.a.a.b";
List<XElement> results3 = ProcessData(input3);
}
static List<XElement> ProcessData(string input)
{
StringReader reader = new StringReader(input);
string inputLine = "";
List<List<string>> properties = new List<List<string>>();
while ((inputLine = reader.ReadLine()) != null)
{
properties.Add(inputLine.Split(new char[] { '.' }).ToList());
}
List<XElement> results = Recursive(properties);
return results;
}
static List<XElement> Recursive(List<List<string>> input)
{
List<XElement> results = new List<XElement>();
string parent = input[0][0];
Dictionary<string, List<List<string>>> dict = input.GroupBy(m => m.FirstOrDefault(), n => n)
.ToDictionary(m => m.Key, n => n.Select(p => p.Skip(1).ToList<string>()).ToList());
foreach (string key in dict.Keys)
{
List<List<string>> subChilds = dict[key];
//List<XElement> subElements = new List<XElement>();
for (int i = subChilds.Count() - 1; i >= 0; i--)
{
if (subChilds[i].Count() == 0)
{
subChilds.RemoveAt(i);
}
}
List<XElement> child = null;
if (subChilds.Count() > 0)
{
child = Recursive(subChilds);
//elements.Add(child);
}
results.Add(new XElement(key, child));
}
return results;
}
}
}
After one more day of work, I finally found it :
XmlDocument xml = new XmlDocument();
XmlElement rootNode = xml.CreateElement(Objet_Fin.GetType().Name);
xml.AppendChild(rootNode);
// Pour chaque différence
foreach (Difference diff in Differences) {
string[] composants_diff = diff.PropertyName.Split({ "." }, StringSplitOptions.RemoveEmptyEntries);
XmlElement parentNode = rootNode;
XmlElement currentNode = null;
string currentXPath = "/" + rootNode.Name;
// Pour chaque propriété imbriquée
for (i = 0; i <= composants_diff.Length - 2; i++) {
// Construction du Xpath
currentXPath += "/" + composants_diff(i);
// Selection du node à tester
currentNode = rootNode.SelectSingleNode(currentXPath);
if (currentNode == null) {
// Si le node n'existe pas, on le créé et l'ajoute au parent
currentNode = xml.CreateElement(composants_diff(i));
parentNode.AppendChild(currentNode);
}
parentNode = currentNode;
}
// On écrit la propriété "finale"
XmlNode newNode = xml.CreateElement(composants_diff.Last());
newNode.InnerText = diff.Object2Value;
parentNode.AppendChild(newNode);
}
return xml;
Hope someone will read that one day...

Categories

Resources