Get information from XML file in C# requested in dialog - c#

I'm trying to parse/get the information of an XML file where I have saved the setting values.
I would like to open a dialog, where the user can select the .xml file and after that get the information and load the settings.
The XML file looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<Configuration version="1.2" createDate="2018-07-17T10:00:00">
<AutoScale>1</Autoscale>
<Threshold>2142</Threshold>
<MinAuto>14</MinAuto>
<MinMan>1</MinMan>
<MaxMan>1</MaxMan>
<BlueBackground>1</BlueBackground>
<Contour>1</Contour>
<Rotate>180</Rotate>
<Flip>Vertical</Flip>
</Configuration>
My code (in C#) looks like this:
using (var openFileDialogXML = new OpenFileDialog()){
System.IO.Stream myStream = null;
openFileDialogXML.InitialDirectory = #System.Environment.CurrentDirectory;
openFileDialogXML.Filter = "xml files (*.xml)|*.xml|All files (*.*)|*.*";
openFileDialogXML.FilterIndex = 1;
openFileDialogXML.RestoreDirectory = true;
DialogResult dr = openFileDialogXML.ShowDialog();
if (dr == System.Windows.Forms.DialogResult.OK)
{
using (XmlReader reader = XmlReader.Create(openFileDialogXML.FileName))
{
reader.MoveToContent();
var version = reader.GetAttribute("version");
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "AutoScale":
//Get AutoScale value
break;
case "Threshold":
break;
case "MinAuto":
break;
case "MinMan":
break;
case "MaxMan":
break;
}
}
}
}
I'm open to use any parser but I would like to read it element by element because it could happen that we add new settings in the future.
Can you please help me/ give me some advice about how I can reach this?

I like using Xml Linq and putting results into a dictionary so when new items are added the xml parser doesn't have to change :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication53
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Dictionary<string, string> dict = doc.Element("Configuration").Elements()
.GroupBy(x => x.Name.LocalName, y => (string)y)
.ToDictionary(x => x.Key, y => y.FirstOrDefault());
}
}
}

I would suggest to use DataContract and load the XML into specified object. When your configuration file changes, you would need to update also the Entity.
[DataContract]
public class MyXmlClass
{
[DataMember]
public int PropertyToSerialize { get; set; }
}
You can then use DataContractSerializer as described here - https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/serialization-and-deserialization
It will be much easier for you to work with object than parsing XML manually :)

Some quick and dirty answer if you want to parse it manually:
using System.Xml;
[...]
XmlTextReader xtr = new XmlTextReader(GetResourceStream("config.xml"));
while (xtr.Read())
{
if (xtr.AttributeCount == 0)
continue;
if (xtr.LocalName == "Configuration")
{
string version = xtr.GetAttribute("version");
string date = xtr.GetAttribute("createDate");
Console.WriteLine($"version={version} - date = {date}")
}
else if (xtr.LocalName == "AutoScale")
{
string autoscale = xtr.ReadString();
Console.WriteLine($"autoscale={autoscale}")
}
[...]
}
xtr.Close();
I didn't try the code, if you need more start by looking XmlTextReader examples or documentation (stackoverflow should have plenty of them)

Related

Handling C# XML Deserialization with Namespaces

I have been wrestling with deserializing the following XML document:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15">
<w:zoom w:percent="100"></w:zoom>
<w:proofState w:spelling="clean" w:grammar="clean"></w:proofState>
<w:defaultTabStop w:val="720"></w:defaultTabStop>
<w:characterSpacingControl w:val="doNotCompress"></w:characterSpacingControl>
<w:compat>
<w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"></w:compatSetting>
<w:compatSetting w:name="overrideTableStyleFontSizeAndJustification" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
<w:compatSetting w:name="enableOpenTypeFeatures" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
<w:compatSetting w:name="doNotFlipMirrorIndents" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
<w:compatSetting w:name="differentiateMultirowTableHeaders" w:uri="http://schemas.microsoft.com/office/word" w:val="1"></w:compatSetting>
</w:compat>
<w:rsids>
<w:rsidRoot w:val="00B31FC7"></w:rsidRoot>
<w:rsid w:val="00251096"></w:rsid>
<w:rsid w:val="00481AA7"></w:rsid>
<w:rsid w:val="005C6856"></w:rsid>
<w:rsid w:val="00661DE2"></w:rsid>
<w:rsid w:val="00984D97"></w:rsid>
<w:rsid w:val="00A06ADC"></w:rsid>
<w:rsid w:val="00B31FC7"></w:rsid>
</w:rsids>
<m:mathPr>
<m:mathFont m:val="Cambria Math"></m:mathFont>
<m:brkBin m:val="before"></m:brkBin>
<m:brkBinSub m:val="--"></m:brkBinSub>
<m:smallFrac m:val="0"></m:smallFrac>
<m:dispDef></m:dispDef>
<m:lMargin m:val="0"></m:lMargin>
<m:rMargin m:val="0"></m:rMargin>
<m:defJc m:val="centerGroup"></m:defJc>
<m:wrapIndent m:val="1440"></m:wrapIndent>
<m:intLim m:val="subSup"></m:intLim>
<m:naryLim m:val="undOvr"></m:naryLim>
</m:mathPr>
<w:themeFontLang w:val="en-US"></w:themeFontLang>
<w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink"></w:clrSchemeMapping>
<w:shapeDefaults>
<o:shapedefaults v:ext="edit" spidmax="1026"></o:shapedefaults>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1"></o:idmap>
</o:shapelayout>
</w:shapeDefaults>
<w:decimalSymbol w:val="."></w:decimalSymbol>
<w:listSeparator w:val=","></w:listSeparator>
<w15:chartTrackingRefBased></w15:chartTrackingRefBased>
<w15:docId w15:val="{23720E07-DD19-46BC-8098-ED32713AB32B}"></w15:docId>
</w:settings>
I am only interested in what is contained within the rsids element. So I thought I could create classes that looked like this:
[XmlRoot(ElementName ="settings", Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public class rsids
{
[XmlElement(ElementName ="rsids",Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public List<rsid> Rsids { get; set; }
}
public class rsid
{
[XmlAttribute(Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public static string val { get; set; }
}
I am attempting to deserialize like this:
XDocument xdoc = XDocument.Load(file);
using (TextReader reader = new StringReader(xdoc.ToString()))
{
try
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(rsids));
StreamReader sr = new StreamReader(file);
rsids SettingsXml = (rsids)xmlSerializer.Deserialize(sr);
foreach (var rsid in SettingsXml.Rsids)
{
Console.WriteLine(rsid.val.Count());
}
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
}
However, I am getting the following error: "Value cannot be null". This is my first attempt at deserializing an XML document with Namespaces. I have browsed the community and found plenty of articles of folks having similar issues however, after trying some of those solutions I am just as confused as when I started and just going in circles. I want to understand this. Some of those posted solutions out there seem to indicate I only have to add a blank Namespace attribute to my decorators (Namespace ="") and others show the actual namespace uri being referenced but only for the root element leaving blanks in subsequent elements \ attributes. I am more looking for the education as to 'why'\'when' to use one method over another and an example of how to accomplish this given the XML below. I appreciate any help you can provide.
Cheers
You're not too far off.
Your XmlElement attribute implies multiple rsids elements. What you want is a single rsids element containing multiple rsid elements. The easiest way to do this is using the XmlArray and XmlArrayItem attributes.
The val property shouldn't be static
Due to what looks like a bug in XmlSerializer, you need to include Form = XmlSchemaForm.Qualified in your XmlAttribute attribute.
You can also omit most of your Namespace properties as they'll be inherited, and ElementName doesn't have to be specified explicitly.
Putting all that together:
[XmlRoot(Namespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main")]
public class settings
{
[XmlArray("rsids")]
[XmlArrayItem("rsid")]
public List<rsid> Rsids { get; set; }
}
public class rsid
{
[XmlAttribute(Form = XmlSchemaForm.Qualified)]
public string val { get; set; }
}
Of course, if that's all you want then a simple LINQ to XML query would be a lot easier:
XNamespace w = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
var rsids = doc.Descendants(w + "rsid")
.Attributes(w + "val")
.Select(x => x.Value);
See this fiddle for a working demo of both approaches.
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
int[] rsids = doc.Descendants().Where(x => x.Name.LocalName == "rsids").Select(x => new {
rsids = x.Elements().Select(y => int.Parse((string)y.Attribute(x.GetNamespaceOfPrefix("w") + "val"), System.Globalization.NumberStyles.HexNumber))
}).Select(x => x.rsids).FirstOrDefault().Select(x => x).ToArray();
}
}
}

Read Mulitple childs and extract data xmlReader in c#

XML :
<InformationTuples>
<InformationTuple>
<Name>documentClass</Name>
<value format="" valueset="{rechnung}" originalValue="Rechnung" start="0" end="0" LD="0" doc="C:\b4enviam-service-test\inputDir\031a0933-2616-4d8e-8a79-56746ae0e160/Invoice_51029062.pdf">Rechnung</value>
<EntityType>Class</EntityType>
<state>New </state>
<need>Mandatory </need>
<extractionmethod>
</extractionmethod>
<weight>1</weight>
<precondition type="optional">All</precondition>
</InformationTuple>
<InformationTuple>
<Name>SAPNr.</Name>
<value format="" valueset="" originalValue="4352020616" start="0" end="0" LD="0" doc="C:\b4enviam-service-test\inputDir\031a0933-2616-4d8e-8a79-56746ae0e160/Invoice_51029062.pdf">4352020616</value>
<EntityType>KB.GTInovice</EntityType>
<state>New </state>
<need>Mandatory </need>
<extractionmethod>
</extractionmethod>
<weight>1</weight>
<precondition type="optional">all</precondition>
</InformationTuple>
<InformationTuple>
<Name>GT-Invoice</Name>
<value format="" valueset="" originalValue="" start="0" end="0" LD="0" doc="">
</value>
<EntityType>KB.GTInovice</EntityType>
<state>New </state>
<need>Mandatory </need>
<extractionmethod>
</extractionmethod>
<weight>1</weight>
<precondition type="optional">all</precondition>
</InformationTuple>
</InformationTuples>
C#
reader.ReadToFollowing("InformationTuple");
reader2.ReadToFollowing("InformationTuple");
do
{
subtree = reader2.ReadSubtree();
subtree.ReadToFollowing("Name");
Debug.WriteLine(subtree.ReadElementContentAsString());
reader2.ReadToNextSibling("InfromationTuple");
} while (reader.ReadToNextSibling("InformationTuple"))
I'm trying for a while now to extract data from multiple childs in XML using c# but didn't successful. I have tried multiple code snippets but unable to extract data.
Like i have to extract the data given in three information tuples, but functions given in the XMLreader not working properly reader pointer break after single loop iteration (unable to move to second InformationTuple), even i have tried two different reader pointer but its now giving exception.
Need little help ,
Thanks
You can read the first <Name> element inside each <InformationTuple> as follows. Introduce the following extension methods:
public static partial class XmlReaderExtensions
{
public static IEnumerable<string> ReadAllElementContentsAsString(this XmlReader reader, string localName, string namespaceURI)
{
while (reader.ReadToFollowing(localName, namespaceURI))
yield return reader.ReadElementContentAsString();
}
public static IEnumerable<XmlReader> ReadAllSubtrees(this XmlReader reader, string localName, string namespaceURI)
{
while (reader.ReadToFollowing(localName, namespaceURI))
using (var subReader = reader.ReadSubtree())
yield return subReader;
}
}
And then do:
foreach (var name in reader.ReadAllSubtrees("InformationTuple", "")
.Select(r => r.ReadAllElementContentsAsString("Name", "").First()))
{
// Process the name somehow
Debug.WriteLine(name);
}
If you want to only read the first <Name> element of each <InformationTuple> element inside each <InformationTuples> container, you can restrict the scope of the search by composing calls to ReadAllSubtrees() using SelectMany():
foreach (var name in reader.ReadAllSubtrees("InformationTuples", "")
.SelectMany(r => r.ReadAllSubtrees("InformationTuple", ""))
.Select(r => r.ReadAllElementContentsAsString("Name", "").First()))
{
// Process the name somehow
Debug.WriteLine(name);
}
Some notes:
You don't close (or dispose) your ReadSubtree() subtree reader when you are done with it. From the docs:
You should not perform any operations on the original reader until the new reader has been closed. This action is not supported and can result in unpredictable behavior.
Thus you must close or dispose this nested reader before advancing the outer reader.
You have too many calls to reader.ReadToFollowing("InformationTuple"); at the beginning. Perhaps you meant to do reader.ReadToFollowing("InformationTuples");?
To ensure there is one and only one <Name> element for each <InformationTuple> replace First() with .Single().
If there might be multiple <Name> nodes for each <InformationTuple> and you want to read all of them, do:
foreach (var name in reader.ReadAllSubtrees("InformationTuple", "")
.SelectMany(r => r.ReadAllElementContentsAsString("Name", "")))
{
// Process the name somehow
Demo fiddle here.
The code below I used a lot and will not create any errors.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XmlReader reader = XmlReader.Create(FILENAME);
while (!reader.EOF)
{
if (reader.Name != "InformationTuple")
{
reader.ReadToFollowing("InformationTuple");
}
if (!reader.EOF)
{
XElement subtree = (XElement)XElement.ReadFrom(reader);
Info.info.Add(new Info() { state = (string)subtree.Element("state"), weight = (int)subtree.Element("weight") });
}
}
}
}
public class Info
{
public static List<Info> info = new List<Info>();
public string state { get; set; }
public int weight { get; set; }
}
}

How to do my Silverlight project read my xml file?

I have a big problem. I have to do a quiz in Silverlight with different level of difficulty. I never use this framework and now I try to learn. First, I tried to read my xml file with this framework and I used c# as programming language.
I wrote this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Browser;
using System.Data;
using System.Xml;
using System.Xml.Linq;
using System.IO;
namespace quiz4
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
var quizzes = new List<Quiz>();
var objSettings = new XmlReaderSettings();
objSettings.IgnoreWhitespace = true;
objSettings.IgnoreComments = true;
const string booksFile = #"D:\quiz.xml";
string element = "";
using (XmlReader objReader = XmlReader.Create(booksFile, objSettings))
{
bool isLastElement = false;
var quiz = new Quiz();
var dx = new List<Answers>();
while (objReader.Read())
{
if (objReader.NodeType == XmlNodeType.Element)
{
element = objReader.Name;
if (element == "question")
{
quiz = new Quiz();
dx = new List<Answers>();
isLastElement = true;
}
}
else if (objReader.NodeType == XmlNodeType.Text)
{
switch (element)
{
case "questionText":
quiz.QuestionText = objReader.Value;
//Console.WriteLine("questionText: " + objReader.Value);
break;
case "LEVEL":
quiz.Level = objReader.Value;
//Console.WriteLine("LEVEL " + objReader.Value);
break;
case "correct":
dx.Add(new Answers() { IsCorrect = true, AnswerName = objReader.Value });
//Console.WriteLine("correct: " + objReader.Value);
break;
case "incorrect":
dx.Add(new Answers() { IsCorrect = false, AnswerName = objReader.Value });
//Console.WriteLine("incorrect: " + objReader.Value);
break;
}
}
if (isLastElement)
{
quiz.AnswerList = dx;
quizzes.Add(quiz);
isLastElement = false;
}
}
}
}
class Quiz
{
public string QuestionText;
public string Level;
public List<Answers> AnswerList;//lista de raspunsuri
}
public class Answers
{
public bool IsCorrect;//raspuncul poate fi adevarat(true) sau false.
public string AnswerName;//raspunsul
}
XML file:
<?xml version="1.0" encoding="utf-8" ?>
<quiz>
<question>
<questionText>In Oracle SQL * PLUS, functia LOWER (col/value) permite:</questionText>
<LEVEL>2</LEVEL>
<correct>fortarea caracterelor scrise cu litere mari sau mixte, in caractere scrise cu litere mici</correct>
<incorrect>fortarea caracterelor scrise cu litere mici in caractere scrise cu litere maric)</incorrect>
<incorrect>returnarea numarului de caractere din coloana sau valoarea literalad)</incorrect>
<incorrect>translatarea lungimii caracterelor dintr-o coloana/valoare la o lungime specificata</incorrect>
</question>
<question>
<questionText>In Oracle SQL * PLUS, functia INITCAP permite:</questionText>
<LEVEL>1</LEVEL>
<incorrect>transformarea oricarei litere a unui cuvant, in litera mare</incorrect>
<correct>transformarea primei litere a fiecarui cuvant/coloana in litera mare</correct>
<incorrect>transformarea unei litere specificate a unui cuvant , intr-o litera mare </incorrect>
<incorrect>este o similitudine cu alte SGBD si nu exista specificata in SQL*PLYS</incorrect>
</question>
</quiz>
When I press F5 nothing happends. Why? Can someone help me? Thanks!
As stated in an earlier answer, you can't access files directly from the local files structure in Silverlight. The way you usally do this is to host the file (in your case the quiz.xml) on the web server and let Silverlight do a call to the web server to fetch the file. You can host the file on the same web server that is hosting the silverlight application. This is done asynchronously, and you could initiate this call in the constructor. Another thing is the way you read the xml-file, it looks a bit strange to me, I would prefer to use Linq to XML. So here is what I would do:
public partial class MainPage : UserControl
{
private List<Quiz> quizzes;
public MainPage()
{
InitializeComponent();
var xmlUri = new Uri("http://yoursite.com/quiz.xml");
var downloader = new WebClient();
downloader.OpenReadCompleted += new OpenReadCompletedEventHandler(downloader_OpenReadCompleted);
downloader.OpenReadAsync(xmlUri);
}
void downloader_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
Stream responseStream = e.Result;
using (var reader = XmlReader.Create(responseStream))
{
var doc = XDocument.Read(reader);
quizzes = doc.Descendants("question")
.Select(q => new Quiz
{
QuestionText = q.Element("questionText").Value,
Level = q.Element("LEVEL").Value,
AnswerList = q.Descendants("incorrect")
.Select(i => new Answers
{
IsCorrect = false,
AnswerName = i.Value
})
.Union(
q.Descendants("correct")
.Select(i => new Answers
{
IsCorrect = true,
AnswerName = i.Value
})).ToList()
}).ToList();
}
}
}
}
Silverlight is running in a sandbox in the browser your application does not have access to the local file system (e.g. D:\quiz.xml). You will have to install your Silverlight application as a trusted application to get full access to the local file system.
If you manage to get around this restriction in Silverlight you should make another change to your application. Right now you are reading the XML in the constructor of the MainPage class (and because of the restriction I have described an exception is thrown). You should move this code into and event handler (e.g. for the Loaded event) but you should also make sure that any file system or network access is done using asynchronous methods. E.g., the UI is updated in asynchronous callbacks.
If you are learning C# you will discover that writing a Silverlight application will move focus away from simply learning the language because of the additional restrictions imposed by Silverlight.

OpenXML SDK getting ActiveX controls value

For one of my projects in college I had to create a test as a Word document and add some ActiveX forms in it to be completed by another person. After this I had to programmatically extract the answers from it and put a grade on the test.
I used OpenXML SDK for processing the document, but it gave me headaches because I couldn't find a way to get the ActiveX values.
So what was the solution?
After searching the Internet and a bit of sniffing on the document I have found that the ActiveX control data can be found in a document part specified by the control ID. Determining the type of control is a bit tricky because I didn't find any documentation about this. Apparently you must get the "classid" attribute from a control and try to match it to the classids you know. Below is the code for determining the values for three types of controls. The rest of the ids are marked as not known and you can match them intuitively to the ones you added in the document.
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Xml;
using System.IO;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml.Packaging;
namespace OpenXMLTest
{
class Program
{
const string textBoxId = "{8BD21D10-EC42-11CE-9E0D-00AA006002F3}";
const string radioButtonId = "{8BD21D50-EC42-11CE-9E0D-00AA006002F3}";
const string checkBoxId = "{8BD21D40-EC42-11CE-9E0D-00AA006002F3}";
static void Main(string[] args)
{
string fileName = #"C:\Users\Andy\Desktop\test_l1demo.docx";
using (WordprocessingDocument doc = WordprocessingDocument.Open(fileName, false))
{
foreach (Control control in doc.MainDocumentPart.Document.Body.Descendants())
{
Console.WriteLine();
Console.WriteLine("Control {0}:", control.Name);
Console.WriteLine("Id: {0}", control.Id);
displayControlDetails(doc, control.Id);
}
}
Console.Read();
}
private static void displayControlDetails(WordprocessingDocument doc, StringValue controlId)
{
string classId, type, value;
OpenXmlPart part = doc.MainDocumentPart.GetPartById(controlId);
OpenXmlReader reader = OpenXmlReader.Create(part.GetStream());
reader.Read();
OpenXmlElement controlDetails = reader.LoadCurrentElement();
classId = controlDetails.GetAttribute("classid", controlDetails.NamespaceUri).Value;
switch (classId)
{
case textBoxId:
type = "TextBox";
break;
case radioButtonId:
type = "Radio Button";
break;
case checkBoxId:
type = "CheckBox";
break;
default:
type = "Not known";
break;
}
value = "No value attribute"; //displays this if there is no "value" attribute found
foreach (OpenXmlElement child in controlDetails.Elements())
{
if (child.GetAttribute("name", controlDetails.NamespaceUri).Value == "Value")
{
//we've found the value typed by the user in this control
value = child.GetAttribute("value", controlDetails.NamespaceUri).Value;
}
}
reader.Close();
Console.WriteLine("Class id: {0}", classId);
Console.WriteLine("Control type: {0}", type);
Console.WriteLine("Control value: {0}", value);
}
}
}

Writing Logs to an XML File with .NET

I am storing logs in an xml file...
In a traditional straight text format approach, you would typically just have a openFile... then writeLine method...
How is it possible to add a new entry into the xml document structure, like you would just with the text file approach?
use an XmlWriter.
example code:
public class Quote
{
public string symbol;
public double price;
public double change;
public int volume;
}
public void Run()
{
Quote q = new Quote
{
symbol = "fff",
price = 19.86,
change = 1.23,
volume = 190393,
};
WriteDocument(q);
}
public void WriteDocument(Quote q)
{
var settings = new System.Xml.XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent= true
};
using (XmlWriter writer = XmlWriter.Create(Console.Out, settings))
{
writer.WriteStartElement("Stock");
writer.WriteAttributeString("Symbol", q.symbol);
writer.WriteElementString("Price", XmlConvert.ToString(q.price));
writer.WriteElementString("Change", XmlConvert.ToString(q.change));
writer.WriteElementString("Volume", XmlConvert.ToString(q.volume));
writer.WriteEndElement();
}
}
example output:
<Stock Symbol="fff">
<Price>19.86</Price>
<Change>1.23</Change>
<Volume>190393</Volume>
</Stock>
see
Writing with an XmlWriter
for more info.
One of the problems with writing a log file in XML format is that you can't just append lines to the end of the file, because the last line has to have a closing root element (for the XML to be valid)
This blog post by Filip De Vos demonstrates quite a good solution to this:
High Performance writing to XML Log files (edit: link now dead so removed)
Basically, you have two XML files linked together using an XML-include thing:
Header file:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log [
<!ENTITY loglines SYSTEM "loglines.xml">
]>
<log>
&loglines;
</log>
Lines file (in this example, named loglines.xml):
<logline date="2007-07-01 13:56:04.313" text="start process" />
<logline date="2007-07-01 13:56:25.837" text="do something" />
<logline date="2007-07-01 13:56:25.853" text="the end" />
You can then append new lines to the 'lines file', but (most) XML parsers will be able to open the header file and read the lines correctly.
Filip notes that: This XML will not be parsed correctly by every XML parser on the planet. But all the parsers I have used do it correctly.
The big difference is the way you are thinking about your log data. In plain text files you are indeed just adding new lines. XML is a tree structure however, and you need to think about like such. What you are adding is probably another NODE, i.e.:
<log>
<time>12:30:03 PST</time>
<user>joe</user>
<action>login</action>
<log>
Because it is a tree what you need to ask is what parent are you adding this new node to. This is usually all defined in your DTD (Aka, how you are defining the structure of your data). Hopefully this is more helpful then just what library to use as once you understand this principle the interface of the library should make more sense.
Why reinvent the wheel? Use TraceSource Class (System.Diagnostics) with the XmlWriterTraceListener.
Sorry to post a answer for old thread. i developed the same long time ago. here i like to share my full code for logger saved log data in xml file date wise.
logger class code
using System.IO;
using System.Xml;
using System.Threading;
public class BBALogger
{
public enum MsgType
{
Error ,
Info
}
public static BBALogger Instance
{
get
{
if (_Instance == null)
{
lock (_SyncRoot)
{
if (_Instance == null)
_Instance = new BBALogger();
}
}
return _Instance;
}
}
private static BBALogger _Instance;
private static object _SyncRoot = new Object();
private static ReaderWriterLockSlim _readWriteLock = new ReaderWriterLockSlim();
private BBALogger()
{
LogFileName = DateTime.Now.ToString("dd-MM-yyyy");
LogFileExtension = ".xml";
LogPath= Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Log";
}
public StreamWriter Writer { get; set; }
public string LogPath { get; set; }
public string LogFileName { get; set; }
public string LogFileExtension { get; set; }
public string LogFile { get { return LogFileName + LogFileExtension; } }
public string LogFullPath { get { return Path.Combine(LogPath, LogFile); } }
public bool LogExists { get { return File.Exists(LogFullPath); } }
public void WriteToLog(String inLogMessage, MsgType msgtype)
{
_readWriteLock.EnterWriteLock();
try
{
LogFileName = DateTime.Now.ToString("dd-MM-yyyy");
if (!Directory.Exists(LogPath))
{
Directory.CreateDirectory(LogPath);
}
var settings = new System.Xml.XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true
};
StringBuilder sbuilder = new StringBuilder();
using (StringWriter sw = new StringWriter(sbuilder))
{
using (XmlWriter w = XmlWriter.Create(sw, settings))
{
w.WriteStartElement("LogInfo");
w.WriteElementString("Time", DateTime.Now.ToString());
if (msgtype == MsgType.Error)
w.WriteElementString("Error", inLogMessage);
else if (msgtype == MsgType.Info)
w.WriteElementString("Info", inLogMessage);
w.WriteEndElement();
}
}
using (StreamWriter Writer = new StreamWriter(LogFullPath, true, Encoding.UTF8))
{
Writer.WriteLine(sbuilder.ToString());
}
}
catch (Exception ex)
{
}
finally
{
_readWriteLock.ExitWriteLock();
}
}
public static void Write(String inLogMessage, MsgType msgtype)
{
Instance.WriteToLog(inLogMessage, msgtype);
}
}
Calling or using this way
BBALogger.Write("pp1", BBALogger.MsgType.Error);
BBALogger.Write("pp2", BBALogger.MsgType.Error);
BBALogger.Write("pp3", BBALogger.MsgType.Info);
MessageBox.Show("done");
may my code help you and other :)
Without more information on what you are doing I can only offer some basic advice to try.
There is a method on most of the XML objects called "AppendChild". You can use this method to add the new node you create with the log comment in it. This node will appear at the end of the item list. You would use the parent element of where all the log nodes are as the object to call on.
Hope that helps.
XML needs a document element (Basically top level tag starting and ending the document).
This means a well formed XML document need have a beginning and end, which does not sound very suitable for logs, where the current "end" of the log is continously extended.
Unless you are writing batches of self contained logs where you write everything to be logged to one file in a short period of time, I'd consider something else than XML.
If you are writing a log of a work-unit done, or a log that doesn't need to be inspected until the whole thing has finished, you could use your approach though - simply openfile, write the log lines, close the file when the work unit is done.
For editing an xml file, you could also use LINQ. You can take a look on how here:
http://www.linqhelp.com/linq-tutorials/adding-to-xml-file-using-linq-and-c/

Categories