Using XmlReader to Read XML - c#

I'm having some trouble using XmlReader to read an XML file. I can open and close the file okay (I think), but when it comes to parsing out the information I need, I'm a bit lost. Here is the bit of the file I need to parse:
<?xml version="1.0" encoding="UTF-8"?>
<database name="Dictionary">
<data>
<Translations>
<Translation UniversalAbbv="Enu" lang="en" localization="US" unicode="0">
<Set>
...
</Set>
<Set>
...
</Set>
<Set>
<CaseSensitive value="0" />
<Enums translate="1">
<Enum_Entry ENUM_H="STOPRUN_STOP" EnumID="0" EnumString="Stop" SetID="160" />
<Enum_Entry ENUM_H="STOPRUN_RUN" EnumID="1" EnumString="Run" SetID="160" />
<Enum_Entry ENUM_H="STOPRUN_HOLD " EnumID="2" EnumString="Hold" SetID="160" />
</Enums>
<IncludeFiles_cs name="CSFile" value="StopRun.cs" />
<IncludeFiles_h name="Header" value="NULL" />
<IncludeFiles_java name="Java" value="NULL" />
<SetID value="160" />
<SetName value="Stop Run" />
<TwoSet ENUM_H="STOPRUN_ENUM_SET" />
</Set>
<Set>
...
</Set>
</Translation>
</Translations>
</data>
</database>
I need to find where EnumID="0" or EnumID="1" (or "STOPRUN_STOP" or "STOPRUN_RUN") and respectively pull out the "Stop" or "Run" strings. Here's what I have for code so far:
static class Dictionary
{
static private XmlReader Reader = null;
static public void Open()
{
XML_Generator.Dictionary.Reader = XmlReader.Create(XML_Generator.Program.DictionaryFilename);
}
static public void Close()
{
XML_Generator.Dictionary.Reader.Close();
}
static public void Read()
{
while (Reader.Read())
{
Trace.TraceInformation(XML_Generator.Dictionary.Reader.ReadElementContentAsString()); // <-- This throw an error. :(
}
}
}
I know it's not much, but I'm a bit lost on where to go with this. Any help would be appreciated. Thanks.

Here is example to read xml file using XML Reader
int intCount = 0;
XmlReaderSettings objSettings = new XmlReaderSettings();
objSettings.IgnoreWhitespace = true;
objSettings.IgnoreComments = true;
string booksFile = Server.MapPath("books.xml");
using (XmlReader objReader = XmlReader.Create(booksFile, objSettings))
{
while (objReader.Read())
{
if (objReader.NodeType == XmlNodeType.Element && "Book" == objReader.LocalName)
{
intCount++;
}
if (objReader.NodeType ==XmlNodeType.Text )
{
Response.Write("<BR />" + objReader.Value);
}
}
}
Response.Write(String.Format("<BR /><BR /><BR /><b> Total {0} books.</b>", intCount));

You probably want to take a look at the XpathNavigator. The syntax of it is really easy to use, much easier than doing it with the XMLReader
All you'd need to do to get the EnumID="1" item is //Enums/Enum_Entry[#EnumID=1]

Related

How to Creating Dynamic Object from XML with type and Attribute Names in C#?

<root>
<source>
<fields>
<string propertyName="Status" class="System.String" nullable="true" valueInputType="all" displayName="status" description="Condition" maxLength="150" />
<function methodName="Calculator" displayName="Calculator" includeInCalculations="true">
<parameters>
<input valueInputType="all" class="System.String" type="string" nullable="true" maxLength="256" />
</parameters>
<returns valueInputType="all" class="System.Double" type="numeric" nullable="false" min="-9007199254740992" max="9007199254740992" allowDecimal="true" allowCalculation="true" />
</function>
</fields>
</source>
</root>
I have to generate an object which can resemble this XML like this:
[Field(DisplayName = "Status", Max = 150, Description = "Condition")]
public string Status;
[Method(DisplayName = "Calculator")]
public double Calculator(string st)
{
double num = st.length();
return num;
}
I may not even use the object directly even if I receive the type of Object is fine.
Try something like this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication11
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var fields = GetFields(doc);
}
static object GetFields(XDocument doc)
{
var results = doc.Descendants("fields").Select(x => new
{
Status = (string)x.Element("string").Attribute("propertyName"),
Max = (int)x.Element("string").Attribute("maxLength"),
Description = (string)x.Element("string").Attribute("description"),
Calculator = (double?)x.Element("returns")
}).ToList();
return results;
}
}
}

reading an xmlnode using xmlDocment

how can i read an Id from the below sample XML >>> CatalogItem Id="3212" and OrganizationName
string xmlfile = #"C:\Users\easwaranp\Desktop\test.xml";
ArrayList arrResult = new ArrayList();
string sContent = File.ReadAllText(xmlfile);
StringReader DocumentReader = new StringReader(sContent);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(DocumentReader);
XmlNodeList ReferenceNodes = xmlDoc.GetElementsByTagName("content:Topic");
foreach (XmlNode Node in ReferenceNodes)
{
string sTopicName = Node.ParentNode.ParentNode.Attributes["OrganizationName"].Value;
}
foreach (string s in arrResult)
{
Console.WriteLine(s);
}
Console.Read();
<content type="application/xml">
<CatalogItems xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="sitename.xsd">
<CatalogSource Acronym="ABC" OrganizationName="ABC Corporation" />
<CatalogItem Id="32122" CatalogUrl="urlname">
<ContentItem xmlns:content="sitename.xsd" Title="Department 1">
<content:SelectionSpec ClassList="" ElementList="" />
<content:Language Value="eng" Scheme="ISO 639-2" />
<content:Source Acronym="ABC" OrganizationName="ABC Corporation1" />
<content:Topics Scheme="ABC">
<content:Topic TopicId="1" TopicName="Marketing1" />
<content:Topic TopicId="11" TopicName="Coverage1" />
</content:Topics>
</ContentItem>
</CatalogItem>
<CatalogItem Id="3212" CatalogUrl="urlname">
<ContentItem xmlns:content="sitename.xsd" Title="Department 2">
<content:SelectionSpec ClassList="" ElementList="" />
<content:Language Value="eng" Scheme="ISO 639-2" />
<content:Source Acronym="ABC" OrganizationName="ABC Corporation2" />
<content:Topics Scheme="ABC">
<content:Topic TopicId="2" TopicName="Marketing2" />
<content:Topic TopicId="22" TopicName="Coverage2" />
</content:Topics>
</ContentItem>
</CatalogItem>
<CatalogItem Id="32132" CatalogUrl="urlname">
<ContentItem xmlns:content="sitename.xsd" Title="Department 3">
<content:SelectionSpec ClassList="" ElementList="" />
<content:Language Value="eng" Scheme="ISO 639-2" />
<content:Source Acronym="ABC" OrganizationName="ABC Corporation3" />
<content:Topics Scheme="ABC">
<content:Topic TopicId="3" TopicName="Marketing3" />
<content:Topic TopicId="33" TopicName="Coverage3" />
</content:Topics>
</ContentItem>
</CatalogItem>
</CatalogItems>
</content>
using System;
using System.IO;
using System.Xml;
class GetValue{
public static void Main(){
var xmlDoc = new XmlDocument();
xmlDoc.Load("test.xml");
var xmlRoot = xmlDoc.DocumentElement;
var nsmgr = new XmlNamespaceManager(xmlDoc.NameTable);
nsmgr.AddNamespace("content", "sitename.xsd");
var attr = xmlRoot.SelectSingleNode("descendant::content:Source/#OrganizationName[../../../#Id='3212']", nsmgr);
if(attr == null)
Console.WriteLine("not found!");
else
Console.WriteLine(attr.Value);
}
}
OUTPUT:
ABC Corporation2
XPath is your friend when it comes to tasks like this. I adapted the following code from a tutorial on codeproject.com. I don't know if it works (or will even compile), but it should get you started in the right direction. Also, I'm assuming your talking about C#, but in case I've misunderstood what language you're using, please disregard (a language tag on your question might help).
using System.Xml;
using System.Xml.XPath;
....
string fileName = "test.xml";
XPathDocument doc = new XPathDocument(fileName);
XPathNavigator nav = doc.CreateNavigator();
// Compile a standard XPath expression
XPathExpression expr;
idExpr = nav.Compile("/content/CatalogItems/CatalogItem/#id");
XPathNodeIterator iterator = nav.Select(idExpr);
// Iterate on the node set
try
{
while (iterator.MoveNext())
{
XPathNavigator nav2 = iterator.Current.Clone();
Console.WriteLine("id: " + nav2.Value);
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}

how to check if xml document has a certain list of attributes?

i am experimenting with C# and xml, i am trying to read a XML file i want to validate if "NumberOfDays" , "NumberOfBooks", "NumberOfExam", "CurrentDate" are existing, if missing. i want to add them with there values.
i have the following xmldocument :
<?xml version="1.0" encoding="utf-8" ?>
<MySample>
<Content>
<add key="NumberOfDays" value="31" />
<add key="NumberOfBooks" value="20" />
<add key="NumberOfExam" value="6" />
<add key="CurrentDate" value="15 - Jul - 2011" />
</Content>
</MySample>
i am writing a sample application in c# using
--------Edit--------
thank you AresAvatar for your responce.
but if the value exists i would like to update its value i.e. if let's say
<add key="NumberOfExam" value="" />
i want to change the value to 6
You can get a list of which nodes exist like this:
// Get a list of which nodes exist
XmlDocument doc = new XmlDocument();
doc.LoadXml(myXml);
List<string> existingNodes = new List<string>();
XmlNodeList bookNodes = doc.SelectNodes("/MySample/Content/add");
foreach (XmlNode nextNode in bookNodes)
{
foreach (XmlAttribute nextAttr in nextNode.Attributes)
existingNodes.Add(nextAttr.InnerText);
}
You can add missing nodes like this:
// Add nodes
XmlNode addRoot = doc.SelectSingleNode("/MySample/Content");
XmlElement addme = doc.CreateElement("add");
addme.SetAttribute("NumberOfDays", "31");
addRoot.AppendChild(addme);
You can set the value of existing nodes like this:
// Update a node
foreach (XmlNode nextNode in bookNodes)
{
foreach (XmlAttribute nextAttr in nextNode.Attributes)
{
switch (nextAttr.Name)
{
case "NumberOfDays":
((XmlElement)nextNode).SetAttribute("value", "31");
break;
// etc.
}
}
}
First of all, if you have control over the generated XML (if you make it yourself), avoid using this schema:
<?xml version="1.0" encoding="utf-8" ?>
<MySample>
<Content>
<add key="NumberOfDays" value="31" />
<add key="NumberOfBooks" value="20" />
<add key="NumberOfExam" value="6" />
<add key="CurrentDate" value="15 - Jul - 2011" />
</Content>
</MySample>
It's much easier to use with that schema:
<?xml version="1.0" encoding="utf-8" ?>
<MySample>
<Content>
<Adds>
<NumberOfDays>31<NumberOfDays/>
<NumberOfBooks>20<NumberOfBooks/>
<NumberOfExam>6<NumberOfExam/>
<CurrentDate>5 - Jul - 2011<CurrentDate/>
</Adds>
</Content>
</MySample>
And then:
XmlDocument doc = new XmlDocument();
doc.Load("YourXmlPath");
XmlNode firstNode = doc["MySample"];
if(firstNode != null)
{
XmlNode secondNode = firstNode["Content"];
if(secondNode != null)
{
XmlNode thirdNode = secondNode["Adds"];
if(thirdNode != null)
{
if(thirdNode["NumberOfDays"] == null) //The "NumberOfDays" node does not exist, we create it.
{
XmlElement newElement = doc.CreateElement("NumberOfDays");
newElement.InnerXml = 31;
thirdNode.AppendChild(newElement);
}
//Do the same for the other nodes...
}
}
}
doc.Save("YourXmlPath");
Just remember to check for null on every node, or put the whole block into a try/catch.
And to save the XML once you did your changes.
XmlDocument.Load() function loads the XML in memory, so you can check for any node without making "blind loops".

XmlReader ReadStartElement causes XmlException

I'm writing a file reader using the XmlReader in a Silverlight project. However, I'm getting some errors (specifically around the XmlReader.ReadStartElement method) and it's causing me to believe that I've misunderstood how to use it somewhere along the way.
Basically, here is a sample of the format of the Xml I am using:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<root>
<EmptyElement />
<NonEmptyElement Name="NonEmptyElement">
<SubElement Name="SubElement" />
</NonEmptyElement>
</root>
And here is a sample of some code used in the same way as how I am using it:
public void ReadData(XmlReader reader)
{
// Move to root element
reader.ReadStartElement("root");
// Move to the empty element
reader.ReadStartElement("EmptyElement");
// Read any children
while(reader.ReadToNextSibling("SubEmptyElement"))
{
// ...
}
// Read the end of the empty element
reader.ReadEndElement();
// Move to the non empty element
reader.ReadStartElement("NonEmptyElement"); // NOTE: This is where I get the error.
// ...
}
So, essentially, I am simply trying to read each element and any contained children. The error I get at the highlighted point is as follows:
Error Description
[Xml_InvalidNodeType]
Arguments: None,10,8
Debugging resource strings are unavailable. Often the key and arguments provide sufficient information to diagnose the problem. See http://go.microsoft.com/fwlink/?linkid=106663&Version=4.0.51204.0&File=System.Xml.dll&Key=Xml_InvalidNodeType
Error Stack Trace
at System.Xml.XmlReader.ReadStartElement(String name)
at ----------------
Any advice or direction on this would be greatly appreciated.
EDIT
Since this reader needs to be fairly generic, it can be assumed that the Xml may contain elements that are children of the EmptyElement. As such, the attempt at reading any SubEmptyElements should be valid.
<SubElement/> is not a sibling of <EmptyElement>, so <NonEmptyElement> is going to get skipped entirely, and your call to ReadEndElement() will read the end element </root>. When you try to subsequently read "NonEmptyElement", there are no elements left, and you'll get an XmlException: {"'None' is an invalid XmlNodeType. Line 8, position 1."}
Note also that since <EmptyElement/> is empty, when you ReadStartElement("EmptyElement"), you'll read the whole element, and you won't need to use ReadEndElement().
I'd also recommend that you configure your reader settings to IgnoreWhitespace (if you're not already doing so), to avoid any complications introduced by reading (insignificant) whitespace text nodes when you aren't expecting them.
Try moving the Read of NonEmptyElement up:
public static void ReadData(XmlReader reader)
{
reader.ReadStartElement("root");
reader.ReadStartElement("EmptyElement");
reader.ReadStartElement("NonEmptyElement");
while (reader.ReadToNextSibling("SubEmptyElement"))
{
// ...
}
reader.ReadEndElement(/* NonEmptyElement */);
reader.ReadEndElement(/* root */);
// ...
}
If you just want to skip anything in <EmptyElement>, regardless of whether or not its actually empty, use ReadToFollowing:
public static void ReadData(XmlReader reader)
{
reader.ReadStartElement("root");
reader.ReadToFollowing("NonEmptyElement");
Console.WriteLine(reader.GetAttribute("Name"));
reader.ReadStartElement("NonEmptyElement");
Console.WriteLine(reader.GetAttribute("Name"));
while (reader.ReadToNextSibling("SubEmptyElement"))
{
// ...
}
reader.ReadEndElement(/* NonEmptyElement */);
reader.ReadEndElement(/* root */);
// ...
}
Update: Here's a fuller example with a clearer data model. Maybe this is closer to what you're asking for.
XMLFile1.xml:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<root>
<Person Type="Homeless"/>
<Person Type="Developer">
<Home Type="Apartment" />
</Person>
<Person Type="Banker">
<Home Type="Apartment"/>
<Home Type="Detached"/>
<Home Type="Mansion">
<PoolHouse/>
</Home>
</Person>
</root>
Program.cs:
using System;
using System.Xml;
namespace ConsoleApplication6
{
internal class Program
{
public static void ReadData(XmlReader reader)
{
reader.ReadStartElement("root");
while (reader.IsStartElement("Person"))
{
ReadPerson(reader);
}
reader.ReadEndElement( /* root */);
}
public static void ReadPerson(XmlReader reader)
{
Console.WriteLine(reader.GetAttribute("Type"));
bool isEmpty = reader.IsEmptyElement;
reader.ReadStartElement("Person");
while (reader.IsStartElement("Home"))
{
ReadHome(reader);
}
if (!isEmpty)
{
reader.ReadEndElement( /* Person */);
}
}
public static void ReadHome(XmlReader reader)
{
Console.WriteLine("\t" + reader.GetAttribute("Type"));
bool isEmpty = reader.IsEmptyElement;
reader.ReadStartElement("Home");
if (!isEmpty)
{
reader.Skip();
reader.ReadEndElement( /* Home */);
}
}
private static void Main(string[] args)
{
var settings = new XmlReaderSettings { IgnoreWhitespace = true };
using (var xr = XmlReader.Create("XMLFile1.xml", settings))
{
ReadData(xr);
}
Console.ReadKey();
}
}
}

How to create an XmlMappingSource during runtime?

(Follow-Up-Question to How to change LINQ O/R-M table name/source during runtime?)
I need to change the table source of a LINQ 2 SQL O/R-Mapper table during runtime. To achieve this, I need to create an XmlMappingSource. On command line, I could use SqlMetal to create this mapping file, but I would like to create the mapping file during runtime in memory.
The XmlMappingSource is a simple xml file, looking something like this:
<?xml version="1.0" encoding="utf-8"?>
<Database Name="MyDatabase" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="dbo.MyFirstTable" Member="MyFirstTable">
<Type Name="MyFirstTable">
<Column Name="ID" Member="ID" Storage="_ID" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
<Association Name="WaStaArtArtikel_WaVerPreisanfragen" Member="WaStaArtArtikel" Storage="_WaStaArtArtikel" ThisKey="ArtikelID" OtherKey="ID" IsForeignKey="true" />
</Type>
</Table>
<Table Name="dbo.MySecondTable" Member="MySecondTable">
<Type Name="MySecondTable">
<Column Name="ID" Member="ID" Storage="_ID" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
<Column Name="FirstTableID" Member="FirstTableID" Storage="_FirstTableID" DbType="UniqueIdentifier NOT NULL" />
<Association Name="MySecondTable_MyFirstTable" Member="MyFirstTable" Storage="_MyFirstTable" ThisKey="FirstTableID" OtherKey="ID" IsForeignKey="true" />
</Type>
</Table>
</Database>
This should be possible to create using reflection, for example I can get the database name from a data context like this:
using System.Data.Linq.Mapping;
using System.Xml.Linq;
XDocument mapWriter = new XDocument();
DatabaseAttribute[] catx = (DatabaseAttribute[])typeof(WcfInterface.WaDataClassesDataContext).GetCustomAttributes(typeof(DatabaseAttribute), false);
XElement xDatabase = new XElement("Database");
xDatabase.Add(new XAttribute("Name", catx[0].Name));
mapWriter.Add(xDatabase);
My problem: I can't find good documentation of the mapping, so extracting the necessary information is quite error-prone - maybe someone can point me to good docs of the mapping, or, even better, to a code example how to create the mapping file?
Have you considered using LINQ to Entities, the mapping formats for LINQ to Entities are documented.
Use Damien Guard's Open Source T4 templates. They do everything SQLMetal can do and more, and you'll have the full T4 engine behind you.
I just had same problem, also I had no option to change project as its too late to do this.
I needed to update database name in mapping file in my solution.
This solution works.
My database mapping
<?xml version="1.0" encoding="utf-8"?>
<Database Name="DatabaseName" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="dbo.tblDictionary" Member="TblDictionary">
<Type Name="TblDictionary">
<Column Name="lngRecordID" Member="LngRecordID" Storage="_LngRecordID" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
<Column Name="txtWord" Member="TxtWord" Storage="_TxtWord" DbType="VarChar(50) NOT NULL" CanBeNull="false" />
</Type>
</Table>
</Database>
and finally the code:
class Program
{
static void Main(string[] args)
{
// to get embeded file name you have to add namespace of the application
const string embeddedFilename = "ConsoleApplication3.FrostOrangeMappings.xml";
// load file into stream
var embeddedStream = GetEmbeddedFile(embeddedFilename);
// process stream
ProcessStreamToXmlMappingSource(embeddedStream);
Console.ReadKey();
}
private static void ProcessStreamToXmlMappingSource(Stream stream)
{
const string newDatabaseName = "pavsDatabaseName";
var mappingFile = new XmlDocument();
mappingFile.Load(stream);
stream.Close();
// populate collection of attribues
XmlAttributeCollection collection = mappingFile.DocumentElement.Attributes;
var attribute = collection["Name"];
if(attribute==null)
{
throw new Exception("Failed to find Name attribute in xml definition");
}
// set new database name definition
collection["Name"].Value = newDatabaseName;
//display xml to user
var stringWriter = new StringWriter();
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
mappingFile.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();
stringWriter.GetStringBuilder();
}
Console.WriteLine(stringWriter.ToString());
}
/// <summary>
/// Loads file into stream
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static Stream GetEmbeddedFile(string fileName)
{
var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStream(fileName);
if (stream == null)
throw new Exception("Could not locate embedded resource '" + fileName + "' in assembly");
return stream;
}`

Categories