Accessing XML elements using C# - c#

I'm having the program shown below:
When the user selects a Client, and after specifying the date using the datePicker, the date should be added under the water request for the same user.
We might have more than one request for each client.
The structure of my XML file is as follows:
<clients>
<record>
<Name></Name>
<Surname></Surname>
<Mobile></Mobile>
<Address></Address>
<Request>
<date></date>
</Request>
</record>
</clients>
Can you help me with this one please?
I've walked through this example:
string xtitle = textBox1.Text.ToString();
XDocument xmlDoc = XDocument.Load("Book.xml");
XElement element =
xmlDoc.Root.Elements("book").Where(r => (string)r.Element("Isbn") == xtitle).FirstOrDefault();
if (element != null)
{
element.SetElementValue("Isbn", textBox1.Text);
element.SetElementValue("Book_Name", textBox2.Text);
element.SetElementValue("Price", textBox3.Text);
element.SetElementValue("Date_of_Publication", textBox4.Text);
element.SetElementValue("Number_of", textBox6.Text);
element.SetElementValue("Category", textBox7.Text);
}
xmlDoc.Save("Book.xml");
Supposing that "ISBN" has another subelement, how can I access it?

XDocument xmlDoc = XDocument.Load("Book.xml");
You can use Linq to Xml to get the Request element and add the date node to it.
var requestDate = DateTime.Today; //Get the date from the date picker
var requestElement = xEle.Descendants("Request").FirstOrDefault();
if(requestElement != null)
{
requestElement.Add(new XElement("date", requestDate));
xmlDoc.Save("Book.xml");
}

I think I would prefer to define the corresponding classes and objects and use an XML serializer constructed from them to read and save the file.
You would need to learn how to use the serializable interface. Look here. Using that, you will create classes to hold the data objects that you would read from the xml file. The structure of the classes will match the structure of your xml file, so that you will have a request class that just has a date property. You will also have a "Client" class that has a collection of request objects as one of its properties, along with client name, etc.
Using the outermost class, you will construct an xml serializer which you can then use to deserialize (read from text/xml file into actual objects in memory) and serialize the file (save from the objects in memory back to a text/xml file. This is just a couple of lines of code that you will find here.
In memory, you will have an array or a collection of Client objects. You can then use LINQ to mess with that collection - insert, sort, delete. You can bind your listbox to that collection to display its data.
Hope this helps.

Related

Dynamic XML parsing, data storage, and forms in c#

So I am developing an application that I want to be able to dynamically parse an xml file, grab attributes and populate a form that is created based on what elements are present in the xml file. Then the values can be edited, and written back to the xml file that was originally opened.
I've been able to parse, save values to a database , populate forms, and write back to the original xml file via hard coding, and I think I have an idea for the dynamic "database" structure (a dictionary where the key is a node's name, and the value is a dictionary if I wanna store a node's child node information, or a string if I'm in the furthest nested child node of an element).
I'm stuck on how to grab the information I need and how to store it in the "database" structure I've come up with. I've done research and have seen people use the "dynamic" object and linq. But everything I've seen seems to involve knowing the path names they've needed before run time. Any ideas on how I should be going about actually parsing the file and grabbing data, first off? I think if I can get that figured out, the rest should kind of fall into place.
Say I have this xml
<users>
<user1 age="43">John Doe</user1>
<user2 age="40">Jane Doe</user2>
</users>
An example database setup would look like Dictionary<users, Dictionary<user1, Dictionary<name,John Doe>
Where you could go Key("users").Key("user1").Key("name").Value to get John Doe or something like that.
Any other suggestions for my database setup would also be appreciated
It sounds like what you are looking for is XML serialization. Before you can do that though, you need to simplify your xml. Instead of numbering the <user> elements, you will want to use an attribute that has the value for the user id. For example you can use an id attribute.
<users>
<user id="1" age="43">John Doe</user>
<user id="2" age="40">Jane Doe</user>
</users>
Once you have done this, create a class that you can serialize to this xml.
using System.Xml.Serialization;
[XmlRoot(ElementName = "users")]
public class Users : List<User>
{
}
[XmlType(TypeName = "user")]
public class User
{
[XmlAttribute(AttributeName = "id")]
public int Id { get; set; }
[XmlAttribute(AttributeName = "age")]
public int Age { get; set; }
[XmlText]
public string Name { get; set; }
}
Then all you have to do to load from the file is deserialize.
XmlSerializer serializer = new XmlSerializer(typeof(Users));
Users users = (Users)serializer.Deserialize(File.OpenRead("XMLFile1.xml"));
You can use LINQ to query the Users collection for specific users.
var user1 = users.Where(u => u.Id == 1)
Modify the User objects or add more to the Users collection, then serialize to save changes back to the file.
XmlSerializer serializer = new XmlSerializer(typeof(Users));
serializer.Serialize(File.OpenWrite("XMLFile1.xml"), users);
You could parse xml using XmlDocument class. After parsing you have tree of nodes which you can traverse and show in UI. I don't understand why you want to save xml as distinct records of values in database, just save it as text. Anyway for saving xml you should translate its records back to xml.

Deserialization with options

I am currently achieving serialization of a collection to the file. The results are like how I expect
<Persons>
<Person>
<Identity>1234</Identity>
<Name>asd</Name>
</Person>
<Person>
<Identity>12345</Identity>
<Name>asdd</Name>
</Person>
</Persons>
Now, I don't want to deserialize the whole collection but I want to deserialize an object from the file with some specific options. For example,
object GetPersonWithIdentity(int identity )
{
// what to do here
}
object asd = GetPersonWithIdentity(1234);
// expected Person with Identity "1234" and Name "asd"
Is it be reasonable to deserialize whole collection and find the specific object and return it, or is there any other solution for this?
XML is not seekable so you at least have to read forward till you find the first match. The framework does not support that automatically so you have to do it manually using an XmlReader which is laborious.
If the file is small and/or performance is not an issue, just deserialize everything and be done with it.
If your dataset is big I'd consider moving to some more scalable format like an embedded SQL database. SQL databases have this capability inherently.
You will have to serialize the entire XML file because as usr mentioned, XML is forward only. XmlReader/Writer is essentially a TextReader (Stream) object, and is doing file i/o to read an XML file.
The reason for my seperate answer is I would do the following using an XDocument object:
object GetPersonWithIdentity(int identity )
{
return myXDocumentVaraible.Descendants("Person")
.First(person => person.Element("Identity").Value == identity.ToString());
}

How do I use LINQ to parse XML with child elements of the same name?

Background Information:
In the past, I have been picking up a collection of XML files and iterating through each XML file, parsing it, passing string data to a data transfer object and passing the object into a database.
Before, my XML looked like this.
<messages>
<message>
<title>Red Wall</title>
<summary>This is a good article</summary>
<ISBN>13546846545464</ISBN>
</message>
</messages>
Here, I only have one element. So, I would parse the XML by using LINQ and retrieve the subsequent elements(title, summary, isbn). Then I would initialize/instantiate an object, assign its properties to the values I retrieved, and send it along.
Now my XML looks like this:
<messages>
<message>
<title>Red Wall</title>
<summary>This is a good article</summary>
<ISBN>13546846545464</ISBN>
</message>
<message>
<title>Blue Wall</title>
<summary>This is not a good article</summary>
<ISBN>15648465416</ISBN>
</message>
</messages>
I now have two (or more) elements in my XML file, and for each one I need to 1) identify that there are multiple elements and 2) for each create a separate DTO to hold the data that I parse.
My question is: How do I parse XML with multiple tags and identify each one I encounter as being separate from the other?
Final Note: While parsing, I need to be able to instantiate a DTO to capture the information I get returned back.
Thanks for helping!
Just Grab the element you want and use your select to populate the dto from the child items. Something like this not tested
XElement ele = loaded.Element("messages");
dtos = from item in ele.Descendants("message")
select new DTO() {title = item.Element(title).value ,... };
The select statement above is going to return an IEnumerable<DTO>, which is a sequence of DTO objects. For every message node it finds in the XML, it will create a DTO object and add it to the sequence returned. If your goal is just to iterate over all the DTOs, you're already there. If you actually need a List<DTO>, there is a constructor on the generic List object that takes an IEnumerable<T>, so you could pass in the "dtos" you received from the select statement and have a List.

Merge two XML files, one of which is non-conformant, in C#

I have two XML files which need to be merged into one file. When I try to merge them, I get an error saying that one of them does not conform.
The offending XML file looks something like:
<letter>
<to>
<participant>
<name>Joe Bethersonton</name>
<PostalAddress>Apartment 23R, 11454 Pruter Street</PostalAddress>
<Town>Fargo, North Dakota, USA</Town>
<ZipCode>50504</ZipCode>
</participant>
</to>
<from>
<participant>
<name>Jon Doe</name>
<PostalAddress>52 Generic Street</PostalAddress>
<Town>Romford, Essex, UK</Town>
<ZipCode>RM11 2TH</ZipCode>
</participant>
</from>
</letter>
I am trying to merge the two files using the following code snippet:
try
{
Dataset ds = new DataSet();
Dataset ds2 = new DataSet();
XmlTextReader reader1 = new XmlTextReader("C:\\File1.xml");
XmlTextReader reader2 = new XmlTextReader("C:\\File2.xml");
ds.ReadXml(reader1);
ds2.ReadXml(reader2);
ds.Merge(ds2);
}
catch(System.Exception ex)
{
Console.WriteLine(ex.Message);
}
This gives the following error:
The same table 'participant' cannot be the child table in two nested relations.
The two XML files are both encoded in UTF-16, which makes combining them by a simple text read and write difficult.
My required end result is one XML file with the contents of the first XML file followed by the contents of the second XML file, with a and tag around the whole lot and a header at the top.
Any ideas?
Thanks,
Rik
In my opinion, the XML you provided is just fine. I suggest, you use the following code and don't use the Dataset class at all:
XDocument doc1 = XDocument.Load("C:\\File1.xml");
XDocument doc2 = XDocument.Load("C:\\File2.xml");
var result = new XDocument(new XElement("Root", doc1.Root, doc2.Root));
result will contain a XML document with "Root" as the root tag and then the content of file 1 followed by the content of file 2.
Update:
If you need to use XmlDocument, you can use this code:
XmlDocument doc1 = new XmlDocument();
XmlDocument doc2 = new XmlDocument();
doc1.Load("C:\\File1.xml");
doc2.Load("C:\\File2.xml");
XmlDocument result = new XmlDocument();
result.AppendChild(result.CreateElement("Root"));
result.DocumentElement.AppendChild(result.ImportNode(doc1.DocumentElement, true));
result.DocumentElement.AppendChild(result.ImportNode(doc2.DocumentElement, true));
I suspect the solution is to provide a schema. DataSet.Merge doesn't know what to do with two sets of elements with the same name. It attempts to infer a schema, but that doesn't work out so well here.
According to this thread on MSDN, this is a limitation of the DataSet class:
The DataSet class in .NET 2.0 (Visual Studio 2005) still has the limitation of not supporting different nested tables with the same name. Therefore you will have to introduce an XML transform to pre-process the XML (and schemas) before you load them up into the DataSet.
Of course, the way that's phrased makes it seem like a newer version might have fixed this. Unfortunately, that may not be the case, as the original answer was posted back in 2005.
This knowledge base article seems to indicate that this behavior is "by design", albeit in a slightly different context.
A better explanation of why this behavior is occurring is also given on this thread:
When ADO reads XML into a DataSet, it creates DataTables to contain each type of element it encounters. Each table is uniquely identified by its name. You can't have two different tables named "PayList".
Also, a given table can have any number of parent tables, but only one of its parent relations can be nested - otherwise, a given record would get written to the XML multiple times, as a child of each of its parent rows.
It's extremely convenient that the DataSet's ReadXml method can infer the schema of the DataSet as it reads its input, but the XML has to conform to certain constraints if it's going to be readable. The XML you've got doesn't. So you have two alternatives: you can change the XML, or you can write your own method to populate the DataSet.
If it were me, I'd write an XSLT transform that took the input XML and turned PayList elements into either MatrixPayList or NonMatrixPaylist elements. Then I'd pass its output to the DataSet.
Using XmlDocument or XDocument to read in and manipulate the XML files is another possible workaround. For an example, see Merging two xml files LINQ
I found a solution using Serialization to first infer the schema,
then serialize the schema and remove the relationships contraints (this tricks the DataSet into thinking that IT has created the dataset.), then load this new schema into a DataSet.
This new dataset will be able to load both your xml files.
More details behind this trick:
Serialization Issue when using WriteXML method

Is there a way to take an XmlNode and display its data in a datagrid view?

I have spent about half a day searching for an answer to this question and am slowly becoming frustrated. I am working with a Web service that returns an XmlNode as its response. I would like to be able to take the XML data reference by the node and view it with a data grid view. Does any one know if this is possible?
I am using the following code:
// submit command to webserver
XmlNode response = SubmitToWebserv((XmlElement)IssueNode, state.Get);
// create XML reader to read response
XmlReader reader = new XmlNodeReader(response);
// create a data table to hold response
DataTable dt = new DataTable();
dt.ReadXmlSchema(reader);
// read data into data table
dt.ReadXml(reader);
It throws exception: DataTable does not support schema inference from Xml. The thing is, the schema is included in the XML referenced by response... So I am at a loss.
I suggest you try that using DataSet instead of DataTable. I don't know that it will work, but it makes sense that it would. The schema might have included more than one table.
OTOH, did the XmlNode include a schema at all?
I recommend you look at this in the debugger. In particular, watch to see which node the XmlReader is positioned on. I'm not 100% convinced it will advance as you want, from the wrapping element, to the schema, then to the data.
Also, is there an element wrapping the schema and data:
<node>
<xs:schema/>
<data/>
</node>
Or are the schema and data elements top-level nodes. This would be a fragment.

Categories

Resources