I have an application that works with XML file. It works great if the xml file is present, however there is also a requirement, that the project generates a blank XML file and then writes to it in case the output file is not present.
The xml file has a quite complicated schema, something like the following:
<?xml version="1.0" encoding="UTF-8"?>
<Results>
<Result>
<WorkorderId>45</WorkorderId>
<WorkerId>13</WorkerId>
<DeviceId>38954178</DeviceId>
<Latitude>45.234</Latitude>
<Longitude>19.54</Longitude>
<Note>[all is good]</Note>
<Operations>
<Operation>
<OperationId>23</OperationId>
<Result>Success</Result>
<ParsedInformation>
<Info>parsed data</Info>
<Info>more parsed data</Info>
</ParsedInformation>
</Operation>
<!-- more operations ... -->
</Operations>
</Result>
<!-- more results ... -->
</Results>
I am wondering how would I create a XmlDocument like this, so I can write the results to it? Is there a "best practice" about hard coding the schema somewhere in the project, etc?
Please note that I am new to Xml so any additional information/literature would be very welcome.
I would usually create a set of classes that would contain the data and tag them with appropriate XmlSerializer attributes to make sure they get serialized to the format you are expecting. This is a pretty good resource: http://msdn.microsoft.com/en-us/library/2baksw0z(VS.85).aspx
In your case you would have the following classes (untested):
[XmlRoot("Results")]
public class Results
{
List<Result> results = new List<Result>();
[XmlElement("Result")]
List<Result> Results {get{return results;}}
}
public class Result
{
List<Operation> operations = new List<Operation>();
int WorkorderId {get; set;}
.... other fields
string Note{get;set;}
List<Operation> Operations {get{return operations;}}
}
public class Operation
{
List<string> parsedInformation = new List<string>();
int OperationId {get;set;}
....
[XmlArray("ParsedInformation")]
[XmlArrayItem("Info")]
List<string> ParsedInformation{get{return parsedInformation;}}
}
Later you use XmlSerializer class to serialize it to xml:
XmlSerializer serializer = new XmlSerializer(typeof(Results));
StringBuilder sb = new StringBuilder(); //this will contain the xml
serializer.Serialize(new TextWriter(sb), resultsObj);
Obviously you can also deserialize data from string to object.
Please note that if you have a XSD schema for your xml, you can use xsd.exe tool to generate the code for you.
If you actually have an XML Schema for your document format, then you can use the xsd.exe utility to generate a bunch of XmlSerializer-compatible classes for it - elements become classes, their attributes and children become properties, element sequences become collections, and so on. You'll also get proper types so long as your schema defines them (i.e. xs:int becomes int, and so on). Then you can build an object tree in memory using those classes, and serialize it using XmlSerializer.
To my knowledge you have to build the document node by node. Perhaps you can save an empty tree as template.
Related
Currently I have a working C# program that works as follows:
Accept .xls template with values (xls is manually created by user)
Save the values (matching fields) to the database
Convert and write .xls to XML. Please see below sample output:
Existing XML Structure
Now, what I want to do is:
Read the existing xml (the created xml)
Insert another set of nodes and subnodes (ReleaseLine and sub nodes). It must accept multiple ReleaseLine.
Save/create the new xml with appended nodes. Please see below output:
This is what I'm looking for:
My existing C# program is simple but the XML nodes and hierarchy is bloody deep. I just created the C# code using new XElement method and passing values for each nodes. Then I simply use xmlDocument.Save() method to write the xml.
[Existing XML Program][3]
To add nodes or append content in existing xml-data I´d use Linq to XML.
XElement xml = XElement.Load("file.xml");
xml.Add( new XElement("uberNode",
new XElement("childNode", content),
new XElement("anotherChildNode", content)));
xml.Save("file.xml");
Here are some other related solutions.
Add to specific node (with example):
Following exisiting XML-data:
`<Names>
<Name>
<prename>John</prename>
<lastname>Snow</lastname>
</Name>
<Name>
<prename>Harry</prename>
<lastname>Harry</lastname>
</Name>
</Names>`
Now I want to add an "age"-tag before the first "prename"-tag and a "family"-tag after the first "lastname"-tag.
XElement xml = XElement.Load("file.xml");
var childrens = xml.DescendantsAndSelf().ToArray();
var first_prename = childrens[2];
var first_lastname = childrens[3];
Console.WriteLine(childrens[0]); //prints out the whole content
first_prename.AddBeforeSelf(new XElement("age", 22));
first_lastname.AddAfterSelf(new XElement("family", new XElement("mother", "paula"), new XElement("father", "paul")));
xml.Save("file.xml");
Outcome:
`<Names>
<Name>
<age>22</age>
<prename>John</prename>
<lastname>Snow</lastname>
<family>
<mother>paula</mother>
<father>paul</father>
</family>
</Name>
<Name>
<prename>Harry</prename>
<lastname>Harry</lastname>
</Name>
</Names>`
I was facing the problem and Linq gave me the easiest way to accomplish that!
There are also other similar way e.g. here. But I tried a bit more and DescendantsAndSelf() made it easier for me to go through.
I found an answer to my question, here is the link http://www.xmlplease.com/add-xml-linq
Using XPathSelectElement method, I was able to find the right node and appended new block of XElement.
I am a university student taking a HCI design course, and using C# and WPF for the first time. I have read a little about xml, and it seems like a good way to get input for use in my program. I have an XML file i made that contains a list of houses and there peramaters, like so:
<house>
<Price>400000</price>
<neighborhood>BrentWood</neighborhood>
<description>This is a so and so house, located...</description>
</house>
<house>
<Price>300000</price>
<neighborhood>BrentWood</neighborhood>
<description>This is a so and so house, located...</description>
</house>
And i have a house class like so:
public class house{
public house(int price, string neighborhood, string description){
this.price = price;
this.neighborhood = neighborhood;
this.description = description;
}
public int price;
public string neighborhood;
public string description;
}
I have read a little about xml, but i cant seems to find a tutorial to make a function that takes the xml file as input, and returns a List of newly created house objects. Can anyone show me how this is done? Or maybe suggest a better way of defining the house objects in a file, and loading them as house objects?
This should get you started using LINQ to XML:
XDocument housesXml = XDocument.Load("houses.xml");
List<House> houses =
housesXml.Root.Elements("house")
.Select(h => new House(
int.Parse(h.Element("price").Value),
(string) h.Element("neighborhood"),
(string) h.Element("description")
))
.ToList();
(Also, wrap your <house> elements in an outer <houses></houses> root tag, and take care to match case, <Price></price> should be <price></price>)
Check these link that help you read the XML file in C# and better guide you which way is good to read it fast:
How to read XML from a file by using Visual C#
Using XML in C# in the simplest way
Reading xml fast
First open your file with XmlTextReader class.
XmlTextReader reader = new XmlTextReader ("books.xml");
After you create the XmlTextReader object, use the Read method to read the XML data. The Read method continues to move through the XML file sequentially until it reaches the end of the file, at which point the Read method returns a value of "False."
To process the XML data, each record has a node type that can be determined from the NodeType property.
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element: // The node is an element.
Console.Write("<" + reader.Name);
Console.WriteLine(">");
break;
case XmlNodeType.Text: //Display the text in each element.
Console.WriteLine (reader.Value);
break;
case XmlNodeType. EndElement: //Display the end of the element.
Console.Write("</" + reader.Name);
Console.WriteLine(">");
break;
}
}
On the place of Console.WriteLine fill your house object with reader.name or properties that you have created in the xml file..
Check these for reading XML file from Linq.
LINQ to read XML
Reading XML documents using LINQ to XML
Check this MSDN tutorial .NET Language-Integrated Query for XML Data
Create Properties in your class rather than creating public elements
and constructor for better implementation.
Or you could use the XmlTextReader class
1. Correct your tags. You cannot have opening "Price" and closing "price" they do not match and it will cause errors.
2. You have to have root element in your XML. Your document should start with some element and close with it (root can be houses).
3. You can load objects using Linq 2 XML:
XElement element = XElement.Parse(...) // or XDocument.Load
List<house> myList = (from item in element.Descendants("house")
select new house(Convert.ToInt32(item.Element("price").Value),
item.Element("neighborhood").Value,
item.Element("description").Value)).ToList();
I know there's no built in converter to convert an array of objects to XML. Is there a quick rudimentary way to create a XML out of the array to help me do a LINQ to XML join between this one and another XML I have?
You can use Linq to XML, it is really easy to map from your existing data structures to XML, i.e.:
int[] values = { 1, 2, 17, 8 };
XDocument doc = new XDocument();
doc.Add(new XElement("root", values.Select( x=> new XElement("item", x))));
produces the following output:
<root>
<item>1</item>
<item>2</item>
<item>17</item>
<item>8</item>
</root>
You can always use XmlSerializer to transform a list of C# objects to XML document. The result of the serialization may be customized by using metadata attributes to designate, for example, root nodes or which class property is to be ignored etc... You will definitely need to apply the attributes to make the resulting XML conform as much as possible to your requirements.
Here is a basic tutorial on serializing an Object to XML:
I have included an XML file in my InfoPath form as a secondary data source. The data connection is named Divisions. Below is the structure and content of the file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Divisions>
<Division>
<Name>Business Application Services</Name>
<Abbr>BAS</Abbr>
</Division>
<Division>
<Name>Network Security Services</Name>
<Abbr>NSS</Abbr>
</Division>
<Division>
<Name>Back Office</Name>
<Abbr>BO</Abbr>
</Division>
</Divisions>
What I need to do is query this file using the Abbr of the division to get the Name.
I have used the following code to create a XPathNodeIterator:
XPathNodeIterator XPNIDivisions = this.DataSources["Divisions"].CreateNavigator().Select("/Divisions/Division/Name");
So now, how do I search for the name of the division whose Abbr is 'NSS'? Or is the code I'm using wrong (I'm new to XML manipulation)?
Please note that these abbreviations and names could change (more could be added, or some could be deleted); so I need it to be dynamic, meaning that if I have the Abbr in a variable MyAbbr, I have to look for its Name.
Thanks in advance,
Yusuf
Can you use Linq to Xml?
I was able to get this to work
string name = XDocument.Load("XMLFile1.xml")
.Descendants("Division")
.Where(x => x.Element("Abbr").Value == "NSS")
.First()
.Element("Name")
.Value;
Finally I've been able to find a solution, and I must say it's a bit more complex than Roger's solution; you have to know a bit about XPath expressions.
So what I had to do was just change the select from before
XPathNodeIterator XPNIDivisions = this.DataSources["Divisions"].CreateNavigator().Select("/Divisions/Division/Name");
to
XPathNodeIterator XPNIDivisions = this.DataSources["Divisions"].CreateNavigator().Select("/Divisions/Division[Abbr=\"thevalue\"]");
where thevalue is of course the value you're looking for.
In fact what I did was define a string variable
StrXPathDiv = "/Divisions/Division[Abbr=\"" + thevalue + "\"]";
and then pass it to the Select() method.
Then use the following code to get the value:
if (XPNIDivisions.MoveNext()) //If any record found
{
XPNIDivisions.Current.MoveToChild(XPathNodeType.Element);
XPNavMyDivision.SetValue(XPNIDivisions.Current.Value);
}
else { XPNavMyDivision.SetValue(""); }
where XPNavMyDivision is the navigator for where I need to set the value.
Thank you very much for your time and help Roger; I would have tried your solution if I were sure everyone had .NET 3.5 installed; however I'm quite sure of the opposite, so I had to stick with this.
I have the following set of data
<ids>
<id1 attr1="value1" attr2="value2" />
<id2 attr3="value3" attr4="value4" />
<id3 attr2="value6" attr5="value7" />
</ids>
Basically, it's an XML that can have any node name with any attribute name with any attribute value.
After parsing the XML, I store the attribute data in a Dictionary.
Then I store that same Dictionary as a value with the node name as a key.
So my data structure would be a Dictionary<string, Dictionary<string, string>> (let's give this a variable name called "dict")
So if I wanted to get the value for attr2 in the id1 node, I would do:
string value = dict["id1"]["attr2"];
// value will be value2
I think this is a pretty simple and workable solution for my needs, but there just seems to be this voice at the back of my head telling me that there is a different data structure or simpler solution that I'm missing out on. What does everyone think?
I think your solution is a good one. It will provide very fast lookups, and matches exactly to your domain.
Is your main problem with the nested dictionaries? If so, I would suggest that you not worry about it - using collections of collections is often a very useful tool.
My only complaint would be this: If you're not using this frequently, you're going to be loading a lot of information into a data structure that may be unncessary. If this is for one time lookups, leaving it in XML and using XPath queries may be a more optimal solution than pre-parsing and loading the entire thing into memory. If you're querying this frequently, though, this is a more optimal solution.
How about a class?
public class YourId
{
public string Id { get; set; }
public string Attribute1 { get; set; }
public string Value { get; set; }
}
Then you could create a List and populate it via your xml...
It would be easy to work with and you could use linq with it:
List<YourId> items = GetIdsFromXml();
var query = from i in items
where i.Id == "id1"
select i;
// or...
items.Where(i => i.Attribute == "blah").ToList();
// ect.
Just for grins - what if you kept the XML DOM and found your attributes with XPath queries? That way if you had duplicate node names you could accomodate that.
That XML doesn't look very good. It's not semantic XML at all. Semantic XML would be:
<data>
<item id="id1">
<value name="attr1">value1</value>
<!-- ... -->
</item>
<!-- ... -->
</data>
I know it's bigger, but that's XML for you. The reason I'm even saying this is that if you're not ready to go with semantic XML, you're probably looking for another data format. XML is a little bit bloated by nature. If you're looking for a compact format, have a look at JSON.
Anyways, using semantic XML, I would recommend XPath. Have a look in the MSDN documentation and look at the SelectNodes methods in the DOM objects.
Short example:
XmlDocument doc = new XmlDocument();
doc.Load("data.xml");
// Get a single item.
XmlNode item = doc.SelectSingleNode("//item[#id=myid]");
As long as all of the nodes have unique names, you should be OK. Note that it won't really work for XML like this:
<nodes>
<node id="id1" attr1="value1" attr2="value2" />
<node id="id2" attr3="value3" attr4="value4" />
<node id="id3" attr2="value6" attr5="value7" />
</nodes>
Given that the XML can have any node name and any attribute name I think your current solution is optimal.
Why not to use something that already exists?
Like Simple XML Parser in C#
If you need an XML grammar then create one for your needs. If you need a parser then use one of the many excellent ones provided in the .Net library. If you need to store the document in memory and access it use the DOM and XPath to select nodes. If you don't need any of this, then I would recommend against using XML and instead using something simpler like JSON.
If you need to keep the whole thing in memory, but just the values, then I suggest using the DataSets and loading them with the the XML loaders.