I have a xml file in ClientBin folder with the name XMLFile1.xml.
There are three nodes in file:
<?xml version="1.0" encoding="utf-8" ?>
<People>
<Person FirstName="Ram" LastName="Sita"/>
<Person FirstName="Krishna" LastName="Radha"/>
<Person FirstName="Heer" LastName="Ranjha"/>
</People>
I can read nodes from file like that:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Uri filePath = new Uri("XMLFile1.xml", UriKind.Relative);
WebClient client1 = new WebClient();
client1.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client1_DownloadStringCompleted);
client1.DownloadStringAsync(filePath);
}
void client1_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
{
XDocument doc = XDocument.Parse(e.Result);
IEnumerable<Person> list = from p in doc.Descendants("Person")
select new Person
{
FirstName = (string)p.Attribute("FirstName"),
LastName = (string)p.Attribute("LastName")
};
DataGrid1.ItemsSource = list;
}
}
But i cant append node to this. What i have done yet with XDocument and XMLDocument gave me compile errors. Thanks.
Update : For example I have tried something like that:
string FirstName = "Ferhad";
string LastName = "Cebiyev";
XDocument xmlDoc = new XDocument();
string path = "C:\\Users\\User\Desktop\\temp\\SilverlightApplication3\\SilverlightApplication3.Web\\ClientBin\\XMLFile1.xml";
xmlDoc.Load(path);
xmlDoc.Add(new Person { FirstName=FirstName, LastName = LastName});
xmlDoc.Save(path);
This is the problem:
xmlDoc.Add(new Person { FirstName=FirstName, LastName = LastName});
Two issues:
That tries to add to the root of the document. There's already a root element, so that will fail.
That's trying to add a Personto the document. You want to add an XElement.
So you probably want:
xmlDoc.Root.Add(new XElement("Person",
new XAttribute("FirstName", FirstName),
new XAttribute("LastName", LastName)));
Related
It's my fist time working with XML and I use a basic XML file format like this:
<root>
<staff id="1">
<name>name 1</name>
<phone>123456</phone>
</staff>
<staff id="2">
<name>name 2</name>
<phone>123789</phone>
<phone2>123789</phone2>
</staff>
</root>
some nodes have more elements (phone2 in this case). I want to add (or remove) an element on a node. I'm creating a WinForms in C# that work with this XML. I'm doing:
I read the XML to have a XmlNodeList variable.
From XmlNodeList I get the node that I want modify to a XmlNode variable.
I modify name or phone on XmlNode
I read again the XML file and I update the correct node with the XmlNode variable new info.
My problem is I don't know how add (or remove) the element "phone2" on my XmlNode variable.
program.cs:
public static XmlNode staff;
public static XmlNodeList xnList = GetList();
public static XmlNodeList GetList()
{
XmlNodeList xnList;
XmlDocument doc = new XmlDocument();
doc.Load(path);
xnList = doc.SelectNodes("/root/staff");
return xnList;
}
public static void GetID(string id)
{
foreach (XmlNode xn in xnList)
{
if(xn.Attributes["id"].Value == id)
{
staff = xn;
}
}
}
form1.cs
private void btnOK_Click(object sender, EventArgs e)
{
Program.staff["name"].InnerText = textBoxName.Text;
Program.staff["phone"].InnerText = textBoxPhone.Text;
if (Program.staff.SelectSingleNode("phone2") == null)
{
// here I want to create "phone2" in Program.staff if not exist
// to update XML file later.
Program.staff["phone2"].InnerText = textBoxPhone2.Text;
}
}
I don't find the correct method to do it and maybe it's not the best way to do it, but I accept suggestions...
There are multiple ways to work with XML files. I'll show two options below.
Test.xml:
<root>
<staff id="1">
<name>Name 1</name>
<phone>123456</phone>
</staff>
<staff id="2">
<name>Name 2</name>
<phone>123457</phone>
<phone>123458</phone>
</staff>
</root>
Option 1 (LINQ to XML):
Add the following using directive:
using System.Xml.Linq;
CreateXmlLinq:
private void CreateXmlLinq(string filename)
{
XElement root = new XElement("root",
new XElement("staff", new XAttribute("id", "1"),
new XElement("name", "Name 1"),
new XElement("phone", "123456")),
new XElement("staff", new XAttribute("id", "2"),
new XElement("name", "Name 2"),
new XElement("phone", "123457"),
new XElement("phone", "123458"))
);
root.Save(filename);
}
Usage:
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "XML File (*.xml)|*.xml";
sfd.FileName = "Test.xml";
if (sfd.ShowDialog() == DialogResult.OK)
{
//save to file
CreateXmlLinq(sfd.FileName);
Debug.WriteLine($"Info: Saved to {sfd.FileName}");
}
}
To remove phone number 123458 where staff id = 2:
RemovePhone:
private void RemovePhone(string filename, string id, string phoneNumber)
{
//load from file
XElement root = XElement.Load(filename);
//remove specified phone number
root.Elements("staff").Where(s => s.Attribute("id").Value == id).Elements("phone").Where(p => p.Value == phoneNumber).Remove();
//save to file
root.Save(filename);
}
Option 2 (XML Serialization):
For this approach, we'll use nested classes.
Add the following using directives to each of the classes:
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
You can name the classes whatever you like, I've chosen to prepend the word "Xml". Additionally for the nested classes, I've chosen to append the ancestors' names. In this case, there is only one ancestor (the parent) "root".
XmlRoot
XmlRootStaff: ("XmlRoot" + "Staff)
XmlRoot.cs:
[XmlRoot(ElementName = "root", IsNullable = false)]
public class XmlRoot
{
[XmlElement(ElementName = "staff")]
public List<XmlRootStaff> Staff { get; set; } = new List<XmlRootStaff>();
}
XmlRootStaff.cs:
public class XmlRootStaff
{
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "phone")]
public List<string> Phone { get; set; } = new List<string>();
}
To deserialize the XML (read from file) we'll use the following method:
DeserializeXMLFileToObject:
public static T DeserializeXMLFileToObject<T>(string xmlFilename)
{
T rObject = default(T);
try
{
if (string.IsNullOrEmpty(xmlFilename))
{
return default(T);
}
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);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error (DeserializeXMLFileToObject) - {ex.Message}");
throw;
}
return rObject;
}
Usage (deserialize):
private XmlRoot _root = null;
...
using (OpenFileDialog ofd = new OpenFileDialog())
{
ofd.Filter = "XML File (*.xml)|*.xml";
ofd.FileName = "Test.xml";
if (ofd.ShowDialog() == DialogResult.OK)
{
//deserialize
_root = HelperXml.DeserializeXMLFileToObject<XmlRoot>(ofd.FileName);
}
}
To serialize the XML (write to file) we'll use the following method:
SerializeObjectToXMLFile:
public static void SerializeObjectToXMLFile(object obj, string xmlFilename)
{
try
{
if (string.IsNullOrEmpty(xmlFilename))
{
return;
}//if
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings();
settings.OmitXmlDeclaration = false;
settings.Indent = true;
settings.NewLineHandling = System.Xml.NewLineHandling.Entitize;
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");
//create new instance
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
//write XML to file
serializer.Serialize(xmlWriter, obj, ns);
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Error (SerializeObjectToXMLFile) - {ex.Message}");
throw;
}
}
Usage (serialize):
private XmlRoot _root = null;
...
using (SaveFileDialog sfd = new SaveFileDialog())
{
sfd.Filter = "XML File (*.xml)|*.xml";
sfd.FileName = "Test.xml";
if (sfd.ShowDialog() == DialogResult.OK)
{
//create new instance
_root = new XmlRoot();
//add data
_root.Staff.Add(new XmlRootStaff() { Id = "1", Name = "Name 1", Phone = new List<string>() { "123456" } });
_root.Staff.Add(new XmlRootStaff() { Id = "2", Name = "Name 2", Phone = new List<string>() { "123457", "123458" } });
//serialize - save to file
SerializeObjectToXMLFile(_root, sfd.FileName);
Debug.WriteLine($"Info: Saved to {sfd.FileName}");
}
}
To remove phone number 123458 where staff id = 2:
private void RemovePhone(string id, string phoneNumber)
{
if (_root != null)
{
for (int i = 0; i < _root.Staff.Count; i++)
{
if (_root.Staff[i].Id == id)
{
//remove
_root.Staff[i].Phone.Remove(phoneNumber);
break;
}
}
}
}
Resources:
LINQ to XML overview
XElement.Save Method
How to delete specific nodes from an XElement?
XML serialization
Examples of XML Serialization
Finally I solved changing:
program.cs
public static XmlDocument doc = new XmlDocument(); // ADDED THIS HERE
public static XmlNode staff;
public static XmlNodeList xnList = GetList();
public static XmlNodeList GetList()
{
XmlNodeList xnList;
// REMOVED XmlDocument from HERE
doc.Load(path);
xnList = doc.SelectNodes("/root/staff");
return xnList;
}
public static void GetID(string id)
{
foreach (XmlNode xn in xnList)
{
if(xn.Attributes["id"].Value == id)
{
staff = xn;
}
}
}
form1.cs
private void btnOK_Click(object sender, EventArgs e)
{
Program.staff["name"].InnerText = textBoxName.Text;
Program.staff["phone"].InnerText = textBoxPhone.Text;
if (Program.staff.SelectSingleNode("phone2") == null)
{
XmlElement elem = Program.doc.CreateElement("phone2");
elem.InnerText = textBoxPhone2.Text;
Program.staff.AppendChild(elem);
}
}
I am trying to append my list to an XML file.
I have my c# class PasswordSettings which contains some properties:
public class PasswordSettings {
public string customerRef { get; set; }
public string node { get; set; }
public string name { get; set; }
public string login { get; set; }
public string password { get; set; }
public string fileType { get; set; }
}
I have a list of PasswordSettings like this:
public List<PasswordSettings> Logins = new List<PasswordSettings>();
I now add elements to my object and add the object to my list:
PasswordSettings settings = new PasswordSettings();
settings.customerRef = "abc";
settings.name = "test";
Logins.add(settings);
Now I want to add this list to an XML file so I end up with something like:
<PasswordSettings>
<Logins>
<customerRef>abc</customerRef>
<name>test</name>
</Logins>
</PasswordSettings>
And if I want to add another login, it will append to the XML file, i.e. not replace or overwrite anything, so a new <Logins>
I have tried multiple methods and I either get null pointers or nothings gets written. I suppose the nullpointer could be because the XML file is empty, but I just want it to add this list as an XML structure.
here is a solution to create xml or add a new record, so after you could adapt following what you want:
PasswordSettings settings = new PasswordSettings();
settings.customerRef = "abc";
settings.name = "test";
Logins.Add(settings);
settings = new PasswordSettings();
settings.customerRef = "def";
settings.name = "test1";
Logins.Add(settings);
foreach (var login in Logins)
{
if (!File.Exists(#"e:\Test.xml"))
{
XDocument doc =
new XDocument(
new XElement("PasswordSettings",
new XElement("Logins",
new XElement("customerRef", login.customerRef),
new XElement("name", login.name)
)
)
);
doc.Save(#"e:\Test.xml");
}
else
{
XDocument doc = XDocument.Load(#"e:\Test.xml");
XElement root = doc.Element("PasswordSettings");
IEnumerable<XElement> rows = root.Descendants("Logins");
XElement firstRow = rows.First();
firstRow.AddBeforeSelf(
new XElement("Logins",
new XElement("customerRef", login.customerRef),
new XElement("name", login.name)));
doc.Save(#"e:\Test.xml");
}
}
}
xml output:
<?xml version="1.0" encoding="utf-8"?>
<PasswordSettings>
<Logins>
<customerRef>def</customerRef>
<name>test1</name>
</Logins>
<Logins>
<customerRef>abc</customerRef>
<name>test</name>
</Logins>
</PasswordSettings>
here i add at the beginning of file, if you want to add at the end of file, just do:
XElement firstRow = rows.Last();
firstRow.AddAfterSelf(
new XElement("Logins",
new XElement("customerRef", login.customerRef),
new XElement("name", login.name)));
output:
<?xml version="1.0" encoding="utf-8"?>
<PasswordSettings>
<Logins>
<customerRef>abc</customerRef>
<name>test</name>
</Logins>
<Logins>
<customerRef>def</customerRef>
<name>test1</name>
</Logins>
</PasswordSettings>
I am trying to construct a .xml file of the form
<Orders>
<Id type="System.Int32">1</Id>
<OrderItems>
<OrderItem>
<Id type="System.Int32">321</Id>
<Product type="System.String">Coffee</Product>
</OrderItem>
</OrderItems>
<Client type="System.String">Johnny</Client>
<Orders>
For Order model:
public class Order
{
public int Id { get; set; }
public List<OrderItem> Products { get; set; }
public string Client { get; set; }
}
Here, I create the Order element
public void SaveToFile(IEnumerable<Order> elementsList)
{
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDec = xmlDoc.CreateXmlDeclaration("1.0", "utf-8", string.Empty);
xmlDoc.PrependChild(xmlDec);
XmlElement elemRoot = xmlDoc.CreateElement("Orders");
xmlDoc.AppendChild(elemRoot);
XmlHelper<Order> xmlHelper = new XmlHelper<Order>();
foreach (var order in _orders)
{
xmlHelper.AddNodeToXmlDocument(xmlDoc, elemRoot, order);
}
xmlDoc.PreserveWhitespace = true;
xmlDoc.Save(_filePath);
}
And here, I am trying to construct the sub-elements. It works fine for Id and Client, but when I try to create the order items, I get this error at line document.AppendChild(elemRoot);
public void AddNodeToXmlDocument(XmlDocument document, XmlElement rootElement, object myObject)
{
XmlElement myObjectElement = document.CreateElement(EntityFormatter.GetObjectName(myObject));
foreach (var objectProperty in EntityFormatter.GetPropertiesAndValues(myObject))
{
if ((objectProperty.Value.GetType().FullName).ToString().Contains("System.Collections.Generic.List"))
{
Regex regex = new Regex(#"Models[.][A-Za-z]+");
Match match = regex.Match(objectProperty.Value.ToString());
var elemRoot = document.CreateElement(match.Value.Substring(7));
document.AppendChild(elemRoot);
foreach (var obj in objectProperty.Value.ToString())
{
AddNodeToXmlDocument(document, elemRoot, obj);
}
}
else
{
var elem = document.CreateElement(objectProperty.Key);
elem.SetAttribute("type", objectProperty.Value.GetType().FullName);
elem.InnerText = objectProperty.Value.ToString();
myObjectElement.AppendChild(elem);
}
}
rootElement.AppendChild(myObjectElement);
}
XML specification only allows single root element in a document. document.AppendChild(elemRoot) line in your AddNodeToXmlDocument() method throws exception because root element has been created before in the SaveToFile() method :
.....
XmlElement elemRoot = xmlDoc.CreateElement("Orders");
xmlDoc.AppendChild(elemRoot);
.....
It isn't clear what you're trying to do with the erroneous line, maybe you want to append elemRoot to the previously created root element instead :
.....
var elemRoot = document.CreateElement(match.Value.Substring(7));
document.DocumentElement.AppendChild(elemRoot);
.....
I am loading my data from XML using C# this way:
XmlDocument xmlDoc = new XmlDocument();
TextAsset xmlFile = Resources.Load("levels/" + levelID) as TextAsset;
xmlDoc.LoadXml(xmlFile.text);
XmlNodeList levelsList = xmlDoc.GetElementsByTagName("level");
foreach (XmlNode levelInfo in levelsList)
{
XmlNodeList childNodes = levelInfo.ChildNodes;
foreach (XmlNode value in childNodes)
{
switch (value.Name)
{
case "info":
//levelWidth = getInt(value, 0);
//levelHeight = getInt(value, 1);
break;
}
}
}
And heres XML I am loading:
<?xml version="1.0" encoding="utf-8" ?>
<level>
<info w="1000" h="500"/>
</level>
It works just fine, I am now trying to find best way to load child nodes, inside my level node with multiple points nodes inside
<?xml version="1.0" encoding="utf-8" ?>
<level>
<info w="1000" h="500"/>
<ground>
<point val1="val1" val2="val2"/>
</ground>
</level>
I will be grateful for some guidance how to move in the right direction, thank you.
Using XML Linq
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string xml =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<level>" +
"<info w=\"1000\" h=\"500\"/>" +
"</level>";
XDocument doc = XDocument.Parse(xml);
XElement level = (XElement)doc.FirstNode;
level.Add("ground", new object[] {
new XElement("point", new XAttribute[] {
new XAttribute("val1", "val1"),
new XAttribute("val2", "val2")
})
});
}
}
}
If you need read all points, you can use
var nodeList = Xmldocument.SelectNodes("level/info/ground/point");
SelectNodes return a list of nodes.
I would go for a slidely different way and use a data object. Then you don't have to analyse xml, you just code your data class:
[Serializable()]
public class CLevel
{
public string Info { get; set; }
}
[Serializable()]
public class CDatafile
{
public List<CLevel> LevelList { get; set; }
public CDatafile()
{
LevelList = new List<CLevel>();
}
}
public class DataManager
{
private string FileName = "Data.xml";
public CDatafile Datafile { get; set; }
public DataManager()
{
Datafile = new CDatafile();
}
// Load file
public void LoadFile()
{
if (System.IO.File.Exists(FileName))
{
System.IO.StreamReader srReader = System.IO.File.OpenText(FileName);
Type tType = Datafile.GetType();
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
object oData = xsSerializer.Deserialize(srReader);
Datafile = (CDatafile)oData;
srReader.Close();
}
}
// Save file
public void SaveFile()
{
System.IO.StreamWriter swWriter = System.IO.File.CreateText(FileName);
Type tType = Datafile.GetType();
if (tType.IsSerializable)
{
System.Xml.Serialization.XmlSerializer xsSerializer = new System.Xml.Serialization.XmlSerializer(tType);
xsSerializer.Serialize(swWriter, Datafile);
swWriter.Close();
}
}
Then you can use it to create, save and load the file like this:
DataManager dataMng = new DataManager();
// Create some data
CLevel level = new CLevel();
level.Info = "Testlevel";
dataMng.Datafile.LevelList.Add(level);
// Save to file
dataMng.SaveFile();
// Load from file
dataMng.LoadFile();
So you can do everything in code checked by the compiler. Makes life a lot easier, or what do you think?
I have products in a class that I want to save in a XML file, the products is devided into categoris and I would like to save my products in a XML file formatet like below examlple. Products for category 1 writes under category one and products for category 2 under category two.
Hwo can I write a method that dose that?
Thanks in advance.
<Categories>
<Category ID="1">
<CategoryName>Categoriname1</CategoryName>
<Description>drinks, coffees, beers, ales</Description>
<Products>
<Product>
<ProductName>Coffe</ProductName>
<QuantityPerUnit>15 boxes x 60 bags</QuantityPerUnit>
<UnitPrice>25</UnitPrice>
<UnitsInStock>3</UnitsInStock>
<UnitsOnOrder>0</UnitsOnOrder>
</Product>
<Product>
<ProductName>Chang</ProductName>
<QuantityPerUnit>24 - 12 oz bottles</QuantityPerUnit>
<UnitPrice>19</UnitPrice>
<UnitsInStock>17</UnitsInStock>
<UnitsOnOrder>40</UnitsOnOrder>
</Product>
</Products>
</Category>
<Category ID="2">
<CategoryName>Condiments</CategoryName>
<Description>spreads, and seasonings</Description>
<Products>
<Product>
<ProductName>Productname</ProductName>
You can use LINQ to XML:
http://www.hookedonlinq.com/LINQtoXML5MinuteOverview.ashx
You can use LINQ to XML.
your example would start off like...
var root = new XElement("Categories",
new XElement("Category",
new XAttribute("ID",1),
new XElement("CategoryName", "Categoriname1")
)
);
Your intellisense should help you get the rest
One option is to use LINQ to XML. Assume you have these classes (where I have removed some properties to simplify the example):
class Category {
public Int32 Id { get; set; }
public String Name { get; set; }
public IEnumerable<Product> Products { get; set; }
}
public class Product {
public String Name { get; set; }
}
You can create some test data:
var categories = new[] {
new Category {
Id = 1,
Name = "Category 1",
Products = new[] {
new Product { Name = "Coffee" },
new Product { Name = "Chang" }
}
},
new Category {
Id = 2,
Name = "Condiments",
Products = new[] {
new Product { Name = "Product 1" }
}
}
};
You can then create an XDocument from the test data:
var xmlDocument = new XDocument(
new XElement(
"Categories",
categories.Select(
c => new XElement(
"Category",
new XAttribute("ID", c.Id),
new XElement("CategoryName", c.Name),
new XElement("Products",
c.Products.Select(
p => new XElement(
"Product",
new XElement("ProductName", p.Name)
)
)
)
)
)
)
);
To save it to a file you can use the Save method:
xmlDocument.Save("Categories.xml");
You need to serialize those classes. Create classes marked with Serializable attribute and use XmlSerializer
example:
http://www.dotnetjohn.com/articles.aspx?articleid=173
You could use the XmlSerializer class or the XmlDocument class or the XDocument class or even the XmlTextWriter class.
You may also like to simply serialise the object(s) that you have. You may write a method like this:
public static bool WriteToXMLFile(string fullFileNameWithPath, Object obj, Type ObjectType)
{
TextWriter xr = null;
try
{
XmlSerializer ser = new XmlSerializer(ObjectType);
xr = new StreamWriter(fullFileNameWithPath);
ser.Serialize(xr, obj);
}
catch (Exception ex)
{
throw ex;
}
finally
{
if(xr != null)
xr.Close();
}
return true;
}