Solved

Reading XML into C#

Posted on 2004-09-12
21
1,846 Views
Last Modified: 2013-11-19
This question can be found in many places across the net and the answer usually involves the ReadElementString method.

But... Why would I ask a 500 point question for something that easy you ask??? I've spoken to a lot of people about the data I'm using and they basically don't know because they know that it's awful XML and it's not elemented really.

Well... It's like this.

I need to extract  values like the following and put them into an editor.

Data:

<properties version="1.0">
<string id="name" value="my file"/>
<string id="auth" value="Dan Atkinson"/>
<string id="desc" value="This is Dan's file"/>
<string id="vers" value="1.0"/>
<colour id="bgcl" red="255" green="0" blue=""/>
<real id="bgop" value="0.2"/>
<integer id="cmod" value="1"/>
<colour id="ppcl" red="255" green="255" blue="255"/>
</properties>

I am using Visual C# 2005 Express and it's helping me a lot but I've come unstuck. I've scoured for an answer for this.

Here is what I have...

xtr = new XmlTextReader(openFileDialog1.FileName);
xtr.ReadStartElement();

String strSkinName = xtr.ReadString();
String strAuthor = xtr.ReadElementString();
String strDescription = xtr.ReadElementString();
String strVersionNo = xtr.ReadElementString();
String strParent = xtr.ReadElementString();
String strBgColRed = xtr.ReadElementString();
String strBgColGreen = xtr.ReadElementString();
String strBgColBlue = xtr.ReadElementString();
String strBgOpacity = xtr.ReadElementString();
String strColourMode = xtr.ReadElementString();
String strMenuPopTxtColourRed = xtr.ReadElementString();
String strMenuPopTxtColourGreen = xtr.ReadElementString();
String strMenuPopTxtColourBlue = xtr.ReadElementString();

this.skinName.Text = strSkinName;
this.author.Text = strAuthor;
this.description.Text = strDescription;
this.versionNo.Text = strVersionNo;
this.parent.Text = strParent;
this.bgColRed.Text = strBgColRed;
this.bgColGreen.Text = strBgColGreen;
this.bgColBlue.Text = strBgColBlue;
this.bgOpacity.Text = strBgOpacity;
this.colourMode.Text = strColourMode;
this.menuPopTxtColourRed.Text = strMenuPopTxtColourRed;
this.menuPopTxtColourGreen.Text = strMenuPopTxtColourGreen;
this.menuPopTxtColourBlue.Text = strMenuPopTxtColourBlue;

I already have the corresponding boxes set up in a form and it works ONLY if I convert the data into elements. Otherwise, the xmlTextReader doesn't understand it.

I am aware that some of these are not strings and some are integers and some are real numbers. But I wanted to get at least  a few of the sections reading in the xml before I start tackling the data types.

The data cannot be converted into standard elements (I know it can but the program that utilises it cannot understand it).

Any help on this would be greatly appreciated.

Also, for an added bonus...

Is there any way I can prevent the xmlTextReader accessing the .dtd file online (which points to a 404 address) when it loads? By preventing I mean bypassing the file.

And, as you can see, the two colour id's in the data section are split into RGB values and yet I want them to be shown seperately (in 3 RGB combo boxes (Red, Green, Blue all 0-255)). Is this possible to break it down into this?

Once all that is done I'll then have to recompile it all back into its rubbish form in an xml file. That may or may not be easy but if I can't do it then it will be a seperate question.

Thanks in advance.
0
Comment
Question by:DanBAtkinson
21 Comments
 
LVL 5

Expert Comment

by:tomasX2
ID: 12040410
use the reader.GetAttribute() function...

for example to get the version.... of the properties...

xtr = new XmlTextReader(openFileDialog1.FileName);
xtr.Read();
string s = reader.GetAttribute("version");
0
 
LVL 19

Expert Comment

by:drichards
ID: 12040529
Or scan through the entire file one piece at a time.  Of course, your code would have to do something with the values.  You might have more elegant processing based in attribute id and element name as well.  This is very brute force and assumes elements are always in same order.  If you're converting RGB values to numbers, be careful as (at least in this XML) a blank string is legal color value text.  This will not convert to an int.  Other than the 404 error, does the lack of DTD cause problems since you are not using a validating reader?  It shouldn't care (or even try to get it as far as I know - which isn't far).

    XmlTextReader xtr = new XmlTextReader("..\\..\\XmlFile2.xml");
    xtr.WhitespaceHandling = WhitespaceHandling.Significant;
    xtr.MoveToContent(); // properties
    string xs = xtr.Name;
    xtr.MoveToFirstAttribute(); // version
    xs = xtr.Name;
    xs = xtr.Value;
    xtr.ReadStartElement(); // string
    xtr.MoveToFirstAttribute(); // id
    xs = xtr.Name;
    xs = xtr.Value;
    xtr.MoveToNextAttribute(); // value
    xs = xtr.Name;
    xs = xtr.Value;
    . . .
    xtr.ReadStartElement(); // colour
    xtr.MoveToFirstAttribute(); // id
    xs = xtr.Name;
    xs = xtr.Value;
    xtr.MoveToNextAttribute(); // red
    xs = xtr.Name;
    xs = xtr.Value;
    xtr.MoveToNextAttribute(); // green
    xs = xtr.Name;
    xs = xtr.Value;
    xtr.MoveToNextAttribute(); // blue
    xs = xtr.Name;
    xs = xtr.Value;
    ...
0
 

Author Comment

by:DanBAtkinson
ID: 12042278
Thanks to both of you for your responses.

TomasX2: Your method 'on paper' looks promising but it doesn't work either. It's very unusual as it looks like it should. When loaded, the program reads in the data and its corresponding textbox is blank where it should read the value for 'name' ("myfile").

DRichards: Unfortunately the data is not always in the same order (as some people put them in the wrong order. The end result is always the same though and that they are read but your method will not work.

What i wanted to do is have the RGB value read in and have a 'color picker' (the round palletes with colours on!!!) to choose the colours. This would enable me to reduce the number of comboboxes (by just having a 'color picker' for each value. I looked into this last night but didn't get very far as the reading is still the main sticking point!

As for the dtd... Technically it shouldn't read it but it does. It's annoying and it's getting to be a bit of a problem. Is there a short if statement out there that says something like:

psuedo:

If

First two lines = <xml definer thing> and <doctype:blah blah "http://www.myurl.com"">

Then

Skip to first attribute named <properties> (which is where the parent program reads the data from). I don't know if this is where the startelement comes in or not.

Either way, the program DOES connect to the Internet and DOES try to access the DTD file. I don't know if this is written into the XMLTextReader by default but it sure as hell is annoying!!!
0
 
LVL 14

Accepted Solution

by:
AvonWyss earned 500 total points
ID: 12042332
If you do it with the reader, the order of the elements in the XMl file will be relevant. I think this is a bad idea.

Try this:

XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Read("..\\..\\XmlFile2.xml");
this.skinName.Text = xmlDoc.DocumentElement.SelectSingleNode('string[@id="name"]/@value').Value;
this.author.Text = xmlDoc.DocumentElement.SelectSingleNode('string[@id="auth"]/@value').Value;
this.bgColRed.Text = xmlDoc.DocumentElement.SelectSingleNode('colour[@id="bgcl"]/@red').Value;
this.bgColGreen.Text = xmlDoc.DocumentElement.SelectSingleNode('colour[@id="bgcl"]/@green').Value;
this.bgColBlue.Text = xmlDoc.DocumentElement.SelectSingleNode('colour[@id="bgcl"]/@blue').Value;

I only posteed a few of the items you need so you get the idea; how SelectSingleNode works can be found in the help and online by looking for XPATH.
0
 

Author Comment

by:DanBAtkinson
ID: 12042462
Thanks for your post.

It says 'too many characters in character literal' citing every single one of the arguments in the brackets. ('string[@id="name"]/@value')
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12042479
Ah, sorry, of course. Did too much Delphi programming lately... *g*

Please replace all the single quotes with double quotes, and all the double quotes with single quotes. E.g.:
"string[@id='name']/@value"
That should work!
0
 

Author Comment

by:DanBAtkinson
ID: 12042559
Thanks. That solved that! How do I change the xmlDoc.Read("..\\..\\XmlFile2.xml"); to something so that it doesn't have to read a specific file and actually goes on the results of the openFileDialog1.FileName as I'm not trying to create a new one but edit an old one of the users choosing.

It also says:

'System.Xml.XmlDocument' does not contain a definition for 'Read'
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12042679
Ahm, might be Load()... I should check my code before posting....
0
 

Author Comment

by:DanBAtkinson
ID: 12042845
I believe they call them personal saviours...

You are one of them!!!

Thankyou!

Do you know of a way to bypass the first two lines of the xml at all?

These two:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE propertylist SYSTEM "http://www.myurl.com/dtds/propertylist.dtd">

I still get my firewall telling me it's trying to connect but the file results in a 404 error regardless of whether I let it connect! Somehow I just want to stop the program from trying to read the first two lines. The problem lies in the fact that not all of the xml documents have those first two lines so I can't ask it to skip them!

Thanks again. I think it's safe to say that the initial question has been answered, if you can help with the bypassing then I'll be eternally grateful!
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12042874
Try this (not tested) instead of the xmlDoc.Load() line:

using (StreamReader sr=new StreamReader(yourfilename)) {
     XmlTextReader xmlr=new XmlTextReader(sr);
     xmlr.XmlResolver=null;
     xmlDoc.Load(xmlr);
}
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:DanBAtkinson
ID: 12042929
It doesn't work but I don't think it's fair on you to answer this question and not get anymore points for it so I'll accept your first answer.

It now looks like this:

try {xtr = new XmlTextReader(openFileDialog1.FileName);

XmlDocument xmlDoc=new XmlDocument();

xmlDoc.Load(openFileDialog1.FileName);

this.skinName.Text = xmlDoc.DocumentElement.SelectSingleNode("string[@id='name']/@value").Value;
this.author.Text = xmlDoc.DocumentElement.SelectSingleNode("string[@id='auth']/@value").Value;
this.description.Text = xmlDoc.DocumentElement.SelectSingleNode("string[@id='desc']/@value").Value;
this.versionNo.Text = xmlDoc.DocumentElement.SelectSingleNode("string[@id='vers']/@value").Value;
this.parent.Text = xmlDoc.DocumentElement.SelectSingleNode("flags[@id='prnt']/@value").Value;
this.bgColRed.Text = xmlDoc.DocumentElement.SelectSingleNode("colour[@id='bgcl']/@red").Value;
this.bgColGreen.Text = xmlDoc.DocumentElement.SelectSingleNode("colour[@id='bgcl']/@green").Value;
this.bgColBlue.Text = xmlDoc.DocumentElement.SelectSingleNode("colour[@id='bgcl']/@blue").Value;
this.bgOpacity.Text = xmlDoc.DocumentElement.SelectSingleNode("real[@id='bgop']/@value").Value;
this.colourMode.Text = xmlDoc.DocumentElement.SelectSingleNode("integer[@id='cmod']/@value").Value;
this.menuPopTxtColourRed.Text = xmlDoc.DocumentElement.SelectSingleNode("colour[@id='ppcl']/@red").Value;
this.menuPopTxtColourGreen.Text = xmlDoc.DocumentElement.SelectSingleNode("colour[@id='ppcl']/@green").Value;
this.menuPopTxtColourBlue.Text = xmlDoc.DocumentElement.SelectSingleNode("colour[@id='ppcl']/@blue").Value;

Although the formatting is a lot better of course!!!

Anyway, it works so I thank you a lot.

Thanks also to drichards and tomasX2 who also provided comments and possible solutions!

Definately an 'A' grade for your help! Cheers once again!
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12042937
Thank you. But try this:

XmlDocument xmlDoc=new XmlDocument();
using (StreamReader sr=new StreamReader(openFileDialog1.FileName)) {
     XmlTextReader xmlr=new XmlTextReader(sr);
     xmlr.XmlResolver=null;
     xmlDoc.Load(xmlr);
}
this.skinName.Text = xmlDoc.DocumentElement.SelectSingleNode('string[@id="name"]/@value').Value;

This should really work - your code did not work since you didn't replace the filename by the xtr in the Load() call.
0
 

Author Comment

by:DanBAtkinson
ID: 12042972
I've fixed you doing the Delphi on the string (:p) but it's telling me that type StreamReader could not be found, even though I added it to the region using directives.
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12042989
using System.IO;
using System.Text;

You have both of these?
0
 

Author Comment

by:DanBAtkinson
ID: 12043004
Yes. They're both at the top of form1.cs. Nothing happens.

The error is:

The type or namespace name 'StreamReader' could not be found (are you missing a using directive or an assembly reference?)
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12043010
Does it work if you specify the fully qualified name like this:

using (System.IO.StreamReader sr=new System.IO.StreamReader(openFileDialog1.FileName))
0
 

Author Comment

by:DanBAtkinson
ID: 12043084
Thanks! That works loads!

I tried it with a file that doesn't have one of the strings. This one to be exact:

this.parent.Text = xmlDoc.DocumentElement.SelectSingleNode("flags[@id='prnt']/@value").Value;

Suffice to say it doesn't like it!!! It will read in the data up until it hits that point (as one would normally expect before debugging kicks in). Can I be bloody cheeky and ask if there a short way of stopping a crash occuring if the xml file does not have certain attribute?
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12043103
Of course. SelectSingleNode() will return null if the string does not exist. therefore, try this:

public static string GetValueOrDefault(XmlNode node, string default) {
     return node!=null ? node.Value : default;
}

and then:

this.parent.Text = GetValueOrDefault(xmlDoc.DocumentElement.SelectSingleNode("flags[@id='prnt']/@value"), "");
0
 

Author Comment

by:DanBAtkinson
ID: 12043119
Where do I place

public static string GetValueOrDefault(XmlNode node, string default) {
     return node!=null ? node.Value : default;
}

?
0
 
LVL 14

Expert Comment

by:AvonWyss
ID: 12043133
Well, it's a method, just put it before the method you're using it...
0
 

Author Comment

by:DanBAtkinson
ID: 12043170
No! It goes crazy with no less than 19 errors about invalid tokens (!=), expectig ';' or '=', but not (XmlNode node, string default).

It tells me that public is an invalid method and that static is not valid for it! It goes right down to the fact that the comma after node shouldn't be there!
0

Featured Post

What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
How to connect to a remote mysql server using C#? 9 43
Expando 4 35
ASP.NET Web API or ASP.NET Core MVC? 3 32
XAML: Layout 8 0
Shoutout to Emily Plummer (http://www.experts-exchange.com/members/eplummer26.html) for giving me this article! She did most of it, I just finished it up and posted it for her :)    Introduction In a previous article (http://www.experts-exchang…
Browsers only know CSS so your awesome SASS code needs to be translated into normal CSS. Here I'll try to explain what you should aim for in order to take full advantage of SASS.
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
This tutorial will teach you the core code needed to finalize the addition of a watermark to your image. The viewer will use a small PHP class to learn and create a watermark.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now