Getting XML attribute values - c#

How I read in the specific value of an XML attribute from a node when my XML looks like the following:
<Settings>
<Display_Settings>
<Screen>
<Name Name="gadg" />
<ScreenTag Tag="asfa" />
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
I only know how to read in the inner text value of XML and not the attribute value. For instance, I want the value of X in LocalPosition. This is what I've tried so far;
XmlNodeList nodeList = xmlDoc.GetElementsByTagName("Screen");
foreach (XmlNode nodeInfo in nodeList)
{
XmlNodeList nodeContent = nodeInfo.ChildNodes;
foreach (XmlNode nodeItems in nodeContent)
{
if (nodeItems.Name == "Tag")
{
print("There is a name");
}
if (nodeItems.Name == "LocalPosition")
{
print("TEST");
}
}
}
Though for what I want to do, I think this is the wrong way to go about it. Can someone point in the right direction please?

You can use LINQ to XML:
var xdoc = XDocument.Load(path_to_xml);
int x = (int)xdoc.Descendants("LocalPosition").First().Attribute("X");
Or with XPath
int x = (int)xdoc.XPathSelectElement("//LocalPosition").Attribute("X");

string XValue= nodeItems.Attributes["X"].Value; // will solve your problem.

I was a little confused when I first started parsing XML with C# too! Instead of trying to write it all yourself .NET provides System.XML.Linq and System.XML.Serialization to help us with all of the hard stuff!
This approach is slightly different as we:
Load the XML document into a System.XML.Linq.XDocument,
Deserialize the XDocument into .NET objects that we can use as we please =]
I hope you don't mind, but I tweaked your XML example a little bit. Try to avoid using too many attributes within your elements, as they tend to reduce readability. The first line of the XML sample just highlights the version that is being used.
You should be able to copy and paste the code sample below into a new console application to get a feel for how it works. Just make sure your XML file is in the ..\bin\Debug folder (otherwise you will need to provide a full file path reference to it).
As you can see, in this example your XML elements (Display_Settings, Screen, Name, ScreenTag, and LocalPosition) are all classes that we decorate with XMLElement attributes. These classes just have public properties that are automatically populated when we call Deserialize(). Neat!
The same applies to the X, Y and Z attributes.
There are a bunch of tutorials that can explain this a lot better than me - enjoy! =]
XML Example
<?xml version="1.0"?>
<Settings>
<Display_Settings>
<Screen>
<Name>NameGoesHere</Name>
<ScreenTag>ScreenTagGoesHere</ScreenTag>
<LocalPosition X="12" Y="12" Z="12" />
</Screen>
</Display_Settings>
</Settings>
Complete Code Sample
using System;
using System.Xml.Linq;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create XDocument to represent our XML file
XDocument xdoc = XDocument.Load("XmlFile.xml");
// Create a deserializer and break down our XML into c# objects
XmlSerializer deserializer = new XmlSerializer(typeof(Settings));
// Deserialize into a Settings object
Settings settings = (Settings)deserializer.Deserialize(xdoc.CreateReader());
// Check that we have some display settings
if (null != settings.DisplaySettings)
{
Screen screen = settings.DisplaySettings.Screen;
LocalPosition localPosition = screen.LocalPosition;
// Check to make sure we have a screen tag before using it
if (null != screen.ScreenTag)
{
Console.WriteLine("There is a name: " + screen.ScreenTag);
}
// We can just write our integers to the console, as we will get a document error when we
// try to parse the document if they are not provided!
Console.WriteLine("Position: " + localPosition.X + ", " + localPosition.Y + ", " + localPosition.Z);
}
Console.ReadLine();
}
}
[XmlRoot("Settings")]
public class Settings
{
[XmlElement("Display_Settings")]
public DisplaySettings DisplaySettings { get; set; }
}
public class DisplaySettings
{
[XmlElement("Screen")]
public Screen Screen { get; set; }
}
public class Screen
{
[XmlElement("Name")]
public string Name { get; set; }
[XmlElement("ScreenTag")]
public string ScreenTag { get; set; }
[XmlElement("LocalPosition")]
public LocalPosition LocalPosition { get; set; }
}
public class LocalPosition
{
[XmlAttribute("X")]
public int X { get; set; }
[XmlAttribute("Y")]
public int Y { get; set; }
[XmlAttribute("Z")]
public int Z { get; set; }
}
}

Try with nodeItems.Attributes["X"].Value

Related

How to remove empty namespace attribute on manually added xml string when serializing object?

I am using XmlSerializer to output my object model to XML. Everything works very well but now I need to add several lines of pre-built XML to the object without building classes for each line. After lots of searching, I found that I can convert the xml string to an XmlElement using XmlDocument's LoadXml and DocumentElement calls. I get the XML I want except that the string section has an empty namespace. How can I eliminate the empty namespace attribute? Is there a better way to add an xml string to the object and have it be serialized properly?
Note: I am only creating output so I don't need to deserialize the generated XML. I am fairly new to the C#, .NET world, and hence, XmlSerialize.
Here is my code:
public class Book
{
public string Title { get; set; }
public string Author { get; set; }
public XmlElement Extension { get; set; }
public Book()
{
}
public void AddExtension()
{
string xmlString = "<AdditionalInfo>" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";
this.Extension = GetElement(xmlString);
}
public static XmlElement GetElement(string xml)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.DocumentElement;
}
}
static void Main(string[] args)
{
TestSerialization p = new TestSerialization();
Book bookOne = new Book();
bookOne.Title = "How to Fix Code";
bookOne.Author = "Dee Bugger";
bookOne.AddExtension();
System.Xml.Serialization.XmlSerializer serializer = new XmlSerializer(typeof(Book), "http://www.somenamespace.com");
using (var writer = new StreamWriter("C:\\BookReport.xml"))
using (var xmlWriter = XmlWriter.Create(writer, new XmlWriterSettings { Indent = true }))
{
serializer.Serialize(xmlWriter, bookOne);
}
}
Here is my output:
<?xml version="1.0" encoding="utf-8"?>
<Book xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.somenamespace.com">
<Title>How to Fix Code</Title>
<Author>Dee Bugger</Author>
<Extension>
<AdditionalInfo xmlns="">
<SpecialHandling>Some Value</SpecialHandling>
</AdditionalInfo>
</Extension>
</Book>
It is the xmlns="" on AdditionalInfo that I want to eliminate. I believe this coming out because there is no association between the XmlDocument I created and the root serialized object, so the XmlDocument creates its own namespace. How can I tell the XmlDocument (and really, the generated XmlElement) that it belongs to the same namespace as the serialized object?
This is added because the parent elements have a namespace and your AdditionalInfo element does not. The xmlns="" attribute changes the default namespace for that element and its children.
If you want to get rid of it, then presumably you want the AdditionalInfo element to have the same namespace as its parent. In which case, you need to change your XML to this:
string xmlString = #"<AdditionalInfo xmlns=\"http://www.somenamespace.com\">" +
"<SpecialHandling>Some Value</SpecialHandling>" +
"</AdditionalInfo>";

Reading large XML files with C#

I would like to know how can I read a XML file from my desktop and put it into a string?
Here is my XML:
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
I want to store each user, but still detect if their small or big, is there a way to do this?
Before you downrate this, you might want to check google because I did research, but found nothing.
"Before you downrate this, you might want to check google because I
did research, but found nothing"
You found nothing because you don't know what you are searching for, also your XML is invalid, you need to enclose it in a rootElement. Then the first thing you need to do is read this file from the desktop if it exists.
You can check the size if you wish at that time and determine if this is "too large" even though it doesn't really matter. I highly doubt your XML file will be 5+ GB in size. If it is then you need an alternative, no single object in a .Net program may be over 2GB, the best you could do is 1,073,741,823 on a 64bit machine.
For very large XML files, anything above 1.0 GB, combine XmlReader and LINQ as stated by Jon Skeet here:
If your document is particularly huge, you can combine XmlReader and
LINQ to XML by creating an XElement from an XmlReader for each of your
"outer" elements in a streaming manner: this lets you do most of the
conversion work in LINQ to XML, but still only need a small portion of
the document in memory at any one time.
For small XML files, anything 1.0 GB or lower stick to the DOM as shown below.
With that said, what you need is to learn what Serialization and Deserialization mean.
Serialize convert an object instance to an XML document.
Deserialize convert an XML document into an object instance.
Instead of XML you can also use JSON, binary, etc.
In your case this is what can be done to Deserialize this XML document back into an Object in order for you to use in your code.
First fix up the XML and give it a Root.
<?xml version="1.0" encoding="UTF-8"?>
<DataRoot>
<smallusers>
<user id="1">
<name>John</name>
<motto>I am john, who are you?</motto>
</user>
<user id="2">
<name>Peter</name>
<motto>Hello everyone!</motto>
</user>
</smallusers>
<bigusers>
<user id="3">
<name>Barry</name>
<motto>Earth is awesome</motto>
</user>
</bigusers>
</DataRoot>
Then create the root class in C#, you may generate this directly in Visual Studio 2012+ by copying your XML and going to Edit - Paste Special, but I like to use: XML to C# Class Generator
Here is what your code would look like after you generate the C# Root Class for your XML, hope it helps you understand it better.
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
namespace ConsoleApplication1
{
public class Program
{
[XmlRoot(ElementName = "user")]
public class User
{
[XmlElement(ElementName = "name")]
public string Name { get; set; }
[XmlElement(ElementName = "motto")]
public string Motto { get; set; }
[XmlAttribute(AttributeName = "id")]
public string Id { get; set; }
}
[XmlRoot(ElementName = "smallusers")]
public class Smallusers
{
[XmlElement(ElementName = "user")]
public List<User> User { get; set; }
}
[XmlRoot(ElementName = "bigusers")]
public class Bigusers
{
[XmlElement(ElementName = "user")]
public User User { get; set; }
}
[XmlRoot(ElementName = "DataRoot")]
public class DataRoot
{
[XmlElement(ElementName = "smallusers")]
public Smallusers Smallusers { get; set; }
[XmlElement(ElementName = "bigusers")]
public Bigusers Bigusers { get; set; }
}
static void Main(string[] args)
{
string testXMLData = #"<DataRoot><smallusers><user id=""1""><name>John</name><motto>I am john, who are you?</motto></user><user id=""2""><name>Peter</name><motto>Hello everyone!</motto></user></smallusers><bigusers><user id=""3""><name>Barry</name><motto>Earth is awesome</motto></user></bigusers></DataRoot>";
var fileXmlData = File.ReadAllText(#"C:\XMLFile.xml");
var deserializedObject = DeserializeFromXML(fileXmlData);
var serializedToXML = SerializeToXml(deserializedObject);
//I want to store each user, but still detect if their small or big, is there a way to do this?
foreach (var smallUser in deserializedObject.Smallusers.User)
{
//Iterating your collection of Small users?
//Do what you need here with `smalluser`.
var name = smallUser.Name; //Example...
}
Console.WriteLine(serializedToXML);
Console.ReadKey();
}
public static string SerializeToXml(DataRoot DataObject)
{
var xsSubmit = new XmlSerializer(typeof(DataRoot));
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw))
{
xsSubmit.Serialize(writer, DataObject);
var data = sw.ToString();
writer.Flush();
writer.Close();
sw.Flush();
sw.Close();
return data;
}
}
}
public static DataRoot DeserializeFromXML(string xml)
{
var xsExpirations = new XmlSerializer(typeof(DataRoot));
DataRoot rootDataObj = null;
using (TextReader reader = new StringReader(xml))
{
rootDataObj = (DataRoot)xsExpirations.Deserialize(reader);
reader.Close();
}
return rootDataObj;
}
}
}

How to convert XML/JSON file to C# class?

I have XML file like this:
<?xml version="1.0"?>
<catalog>
<book id="1" date="2012-02-01">
<title>XML Developer's Guide</title>
<price>44.95</price>
<description>
An in-depth look at creating applications
with XML.
</description>
</book>
<book id="2" date="2013-10-16">
<author>Mark Colsberg</author>
<title>Dolor sit amet</title>
<price>5.95</price>
<description>Lorem ipsum</description>
</book>
</catalog>
How to quick convert it to C# classes to use access data by LINQ?
Do I have to write the class manually for any XML file case?
What about JSON format?
Is the XSD the only solution?
You have two possibilities.
Method 1. XSD tool
Suppose that you have your XML file in this location C:\path\to\xml\file.xml
Open Developer Command Prompt
You can find it in Start Menu > Programs > Microsoft Visual Studio 2012 > Visual Studio Tools
Or if you have Windows 8 can just start typing Developer Command Prompt in Start screen
Change location to your XML file directory by typing cd /D "C:\path\to\xml"
Create XSD file from your xml file by typing xsd file.xml
Create C# classes by typing xsd /c file.xsd
And that's it! You have generated C# classes from xml file in C:\path\to\xml\file.cs
Method 2 - Paste special
Required Visual Studio 2012+
Copy content of your XML file to clipboard
Add to your solution new, empty class file (Shift+Alt+C)
Open that file and in menu click Edit > Paste special > Paste XML As Classes
And that's it!
Usage
Usage is very simple with this helper class:
using System;
using System.IO;
using System.Web.Script.Serialization; // Add reference: System.Web.Extensions
using System.Xml;
using System.Xml.Serialization;
namespace Helpers
{
internal static class ParseHelpers
{
private static JavaScriptSerializer json;
private static JavaScriptSerializer JSON { get { return json ?? (json = new JavaScriptSerializer()); } }
public static Stream ToStream(this string #this)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(#this);
writer.Flush();
stream.Position = 0;
return stream;
}
public static T ParseXML<T>(this string #this) where T : class
{
var reader = XmlReader.Create(#this.Trim().ToStream(), new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
return new XmlSerializer(typeof(T)).Deserialize(reader) as T;
}
public static T ParseJSON<T>(this string #this) where T : class
{
return JSON.Deserialize<T>(#this.Trim());
}
}
}
All you have to do now, is:
public class JSONRoot
{
public catalog catalog { get; set; }
}
// ...
string xml = File.ReadAllText(#"C:\path\to\xml\file.xml");
var catalog1 = xml.ParseXML<catalog>();
string json = File.ReadAllText(#"C:\path\to\json\file.json");
var catalog2 = json.ParseJSON<JSONRoot>();
Here you have some Online XML <--> JSON Converters: Click
You can follow this simple step
1.Please Add using System.Xml as a reference;
2.Make a class named book in this way
public class book
{
public Nullable<System.DateTime> date{ get; set; }
public decimal price { get; set; }
public string title { get; set; }
public string description { get; set; }
}
try
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("Write down full path");
XmlNodeList dataNodes = xmlDoc.SelectNodes("/catalog");
foreach (XmlNode node in dataNodes)
{
book objbook = new book();
objbook.date=Convert.ToDateTime(node.Attributes["date"].Value);
objbook.title=node.SelectSingleNode("title").InnerText;
objbook.description=node.SelectSingleNode("description").InnerText;
objbook.price=Convert.ToDecimal(node.SelectSingleNode("price").InnerText);
}
}
catch(Exception ex)
{
throw ex;
}
Use the XML Schema Definition Tool xsd.exe found in your framework tools to convert your schema into a serializable class or dataset.
xsd file.xsd {/classes | /dataset} [/element:element]
[/language:language] [/namespace:namespace]
[/outputdir:directory] [URI:uri]
And in example, whereas the C# class will be generated in the same directory as the xsd tool:
xsd /c YourFile.xsd
Use the super simple way using 'Paste XML As Classes' functionality in Visual studio menu.
1.copy the xml source in the clipboard, something like CTRL+A and CTRL+C
2.Go to 'Edit' Menu -> Paste Special -> Paste XML As Classes, to paste the generated classes based on the source xml"
Ref: More steps in detail at this link
Here is one another easy way to parse xml file using Cinchoo ETL, an open source library
Define POCO class as below
public class Book
{
[ChoXPath("#id")]
public int Id { get; set; }
[ChoXPath("#date")]
public DateTime Date { get; set; }
[ChoXPath("author")]
public string Author { get; set; }
[ChoXPath("title")]
public string Title { get; set; }
[ChoXPath("price")]
public double Price { get; set; }
[ChoXPath("description")]
public string Description { get; set; }
}
Use it to the parser as below
using (var r = new ChoXmlReader<Book>("*** Xml file path ***")
.WithXPath("//catalog/book", true)
)
{
foreach (var rec in r)
rec.Print();
}
Sample fiddle: https://dotnetfiddle.net/3UI82F

Object reference not set to an instance of an object - LINQ

I am totally stumped on this one. I am getting a "Object reference not set to an instance of an object." error - but I can't figure out why. This is the code that I have:
public class PlayerProfile
{
public List<Profile> PlayerINfo = new List<Profile>();
public void LoadProfiles(string path)
{
XDocument xmlDoc = XDocument.Load(path);
PlayerINfo = new List<Profile>();
// This is where I get the error:
PlayerINfo = (from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
select new Profile
{
Name = (string)profiles.Element("Name"),
Sex = (string)profiles.Element("Sex"),
Avatar = (string)profiles.Element("Avatar").Attribute("path") ?? "",
Created = (DateTime)profiles.Element("Created")
}).ToList();
}
}
Here is my Profile class:
public class Profile
{
public string Name { get; set; }
public string Sex { get; set; }
public string Avatar { get; set; }
public DateTime Created { get; set; }
}
EDIT - Adding the XML file code:
<?xml version="1.0" encoding="utf-8"?>
<OnlineProfile>
<Player>
<Name>Stacey</Name>
<Sex>Female</Sex>
<Avatar path="/images/Picture.png" />
<Ratio>
<Win>0</Win>
<Loss>0</Loss>
<Abandoned>0</Abandoned>
</Ratio>
<Created>6/19/2011</Created>
</Player>
</OnlineProfile>
from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
This is the problem - OnlineProfile is your root element, just do
from profiles in xmlDoc.Root.Elements("Player")
Do this: from profiles in xmlDoc.Element("OnlineProfile").Elements("Player") instead of from profiles in xmlDoc.Root.Element("OnlineProfile").Elements("Player")
From the XML you posted "OnlineProfile" is your Root element, so the child elements you expect arent there.
Tried this out + solved this in visual studio. I see the people above beat me to it. Another way to achieve this is:
xmlDoc.Element("OnlineProfile").Elements("Player")
This is what I posted prior to the Xml becomming available...
here's a good candidate for that error
(string)profiles.Element("Avatar").Attribute("...
").Attribute would cause an error. You need to check for null.
e.g.
= profiles.Element("Avatar") != null ? (string)profiles.Element("Avatar").Attribute("... : null;
do you definitely have an element called Avatar in your Xml file. Did the file load in correctly?

Adding attributes to an XML node

How can I create an xml file dynamically, with the following structure?
<Login>
<id userName="Tushar" passWord="Tushar">
<Name>Tushar</Name>
<Age>24</Age>
</id>
</Login>
I am not able to create the attributes inside the id tag (i.e. userName="" and passWord="").
I am using C# in a windows application.
Some Important namespace that you might require is
using System.Xml;
using System.IO;
Well id isn't really the root node: Login is.
It should just be a case of specifying the attributes (not tags, btw) using XmlElement.SetAttribute. You haven't specified how you're creating the file though - whether you're using XmlWriter, the DOM, or any other XML API.
If you could give an example of the code you've got which isn't working, that would help a lot. In the meantime, here's some code which creates the file you described:
using System;
using System.Xml;
class Test
{
static void Main()
{
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("Login");
XmlElement id = doc.CreateElement("id");
id.SetAttribute("userName", "Tushar");
id.SetAttribute("passWord", "Tushar");
XmlElement name = doc.CreateElement("Name");
name.InnerText = "Tushar";
XmlElement age = doc.CreateElement("Age");
age.InnerText = "24";
id.AppendChild(name);
id.AppendChild(age);
root.AppendChild(id);
doc.AppendChild(root);
doc.Save("test.xml");
}
}
There is also a way to add an attribute to an XmlNode object, that can be useful in some cases.
I found this other method on msdn.microsoft.com.
using System.Xml;
[...]
//Assuming you have an XmlNode called node
XmlNode node;
[...]
//Get the document object
XmlDocument doc = node.OwnerDocument;
//Create a new attribute
XmlAttribute attr = doc.CreateAttribute("attributeName");
attr.Value = "valueOfTheAttribute";
//Add the attribute to the node
node.Attributes.SetNamedItem(attr);
[...]
The latest and supposedly greatest way to construct the XML is by using LINQ to XML:
using System.Xml.Linq
var xmlNode =
new XElement("Login",
new XElement("id",
new XAttribute("userName", "Tushar"),
new XAttribute("password", "Tushar"),
new XElement("Name", "Tushar"),
new XElement("Age", "24")
)
);
xmlNode.Save("Tushar.xml");
Supposedly this way of coding should be easier, as the code closely resembles the output (which Jon's example above does not). However, I found that while coding this relatively easy example I was prone to lose my way between the cartload of comma's that you have to navigate among. Visual studio's auto spacing of code does not help either.
You can use the Class XmlAttribute.
Eg:
XmlAttribute attr = xmlDoc.CreateAttribute("userName");
attr.Value = "Tushar";
node.Attributes.Append(attr);
If you serialize the object that you have, you can do something like this by using "System.Xml.Serialization.XmlAttributeAttribute" on every property that you want to be specified as an attribute in your model, which in my opinion is a lot easier:
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public class UserNode
{
[System.Xml.Serialization.XmlAttributeAttribute()]
public string userName { get; set; }
[System.Xml.Serialization.XmlAttributeAttribute()]
public string passWord { get; set; }
public int Age { get; set; }
public string Name { get; set; }
}
public class LoginNode
{
public UserNode id { get; set; }
}
Then you just serialize to XML an instance of LoginNode called "Login", and that's it!
Here you have a few examples to serialize and object to XML, but I would suggest to create an extension method in order to be reusable for other objects.

Categories