DanBAtkinson
asked on
Reading XML into C#
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(openFileDial og1.FileNa me);
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.T ext = 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.
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(openFileDial
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.T
this.menuPopTxtColourGreen
this.menuPopTxtColourBlue.
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.
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("..\\..\\Xml File2.xml" );
xtr.WhitespaceHandling = WhitespaceHandling.Signifi cant;
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;
...
XmlTextReader xtr = new XmlTextReader("..\\..\\Xml
xtr.WhitespaceHandling = WhitespaceHandling.Signifi
xtr.MoveToContent(); // properties
string xs = xtr.Name;
xtr.MoveToFirstAttribute()
xs = xtr.Name;
xs = xtr.Value;
xtr.ReadStartElement(); // string
xtr.MoveToFirstAttribute()
xs = xtr.Name;
xs = xtr.Value;
xtr.MoveToNextAttribute();
xs = xtr.Name;
xs = xtr.Value;
. . .
xtr.ReadStartElement(); // colour
xtr.MoveToFirstAttribute()
xs = xtr.Name;
xs = xtr.Value;
xtr.MoveToNextAttribute();
xs = xtr.Name;
xs = xtr.Value;
xtr.MoveToNextAttribute();
xs = xtr.Name;
xs = xtr.Value;
xtr.MoveToNextAttribute();
xs = xtr.Name;
xs = xtr.Value;
...
ASKER
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!!!
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!!!
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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"]/@valu e')
It says 'too many characters in character literal' citing every single one of the arguments in the brackets. ('string[@id="name"]/@valu
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!
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!
ASKER
Thanks. That solved that! How do I change the xmlDoc.Read("..\\..\\XmlFi le2.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'
It also says:
'System.Xml.XmlDocument' does not contain a definition for 'Read'
Ahm, might be Load()... I should check my code before posting....
ASKER
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!
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!
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);
}
using (StreamReader sr=new StreamReader(yourfilename)
XmlTextReader xmlr=new XmlTextReader(sr);
xmlr.XmlResolver=null;
xmlDoc.Load(xmlr);
}
ASKER
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(openFileDial og1.FileNa me);
XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load(openFileDialog 1.FileName );
this.skinName.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("strin g[@id='nam e']/@value ").Value;
this.author.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("strin g[@id='aut h']/@value ").Value;
this.description.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("strin g[@id='des c']/@value ").Value;
this.versionNo.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("strin g[@id='ver s']/@value ").Value;
this.parent.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("flags [@id='prnt ']/@value" ).Value;
this.bgColRed.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("colou r[@id='bgc l']/@red") .Value;
this.bgColGreen.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("colou r[@id='bgc l']/@green ").Value;
this.bgColBlue.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("colou r[@id='bgc l']/@blue" ).Value;
this.bgOpacity.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("real[ @id='bgop' ]/@value") .Value;
this.colourMode.Text = xmlDoc.DocumentElement.Sel ectSingleN ode("integ er[@id='cm od']/@valu e").Value;
this.menuPopTxtColourRed.T ext = xmlDoc.DocumentElement.Sel ectSingleN ode("colou r[@id='ppc l']/@red") .Value;
this.menuPopTxtColourGreen .Text = xmlDoc.DocumentElement.Sel ectSingleN ode("colou r[@id='ppc l']/@green ").Value;
this.menuPopTxtColourBlue. Text = xmlDoc.DocumentElement.Sel ectSingleN ode("colou r[@id='ppc l']/@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!
It now looks like this:
try {xtr = new XmlTextReader(openFileDial
XmlDocument xmlDoc=new XmlDocument();
xmlDoc.Load(openFileDialog
this.skinName.Text = xmlDoc.DocumentElement.Sel
this.author.Text = xmlDoc.DocumentElement.Sel
this.description.Text = xmlDoc.DocumentElement.Sel
this.versionNo.Text = xmlDoc.DocumentElement.Sel
this.parent.Text = xmlDoc.DocumentElement.Sel
this.bgColRed.Text = xmlDoc.DocumentElement.Sel
this.bgColGreen.Text = xmlDoc.DocumentElement.Sel
this.bgColBlue.Text = xmlDoc.DocumentElement.Sel
this.bgOpacity.Text = xmlDoc.DocumentElement.Sel
this.colourMode.Text = xmlDoc.DocumentElement.Sel
this.menuPopTxtColourRed.T
this.menuPopTxtColourGreen
this.menuPopTxtColourBlue.
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!
Thank you. But try this:
XmlDocument xmlDoc=new XmlDocument();
using (StreamReader sr=new StreamReader(openFileDialo g1.FileNam e)) {
XmlTextReader xmlr=new XmlTextReader(sr);
xmlr.XmlResolver=null;
xmlDoc.Load(xmlr);
}
this.skinName.Text = xmlDoc.DocumentElement.Sel ectSingleN ode('strin g[@id="nam e"]/@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.
XmlDocument xmlDoc=new XmlDocument();
using (StreamReader sr=new StreamReader(openFileDialo
XmlTextReader xmlr=new XmlTextReader(sr);
xmlr.XmlResolver=null;
xmlDoc.Load(xmlr);
}
this.skinName.Text = xmlDoc.DocumentElement.Sel
This should really work - your code did not work since you didn't replace the filename by the xtr in the Load() call.
ASKER
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.
using System.IO;
using System.Text;
You have both of these?
using System.Text;
You have both of these?
ASKER
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?)
The error is:
The type or namespace name 'StreamReader' could not be found (are you missing a using directive or an assembly reference?)
Does it work if you specify the fully qualified name like this:
using (System.IO.StreamReader sr=new System.IO.StreamReader(ope nFileDialo g1.FileNam e))
using (System.IO.StreamReader sr=new System.IO.StreamReader(ope
ASKER
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.Sel ectSingleN ode("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?
I tried it with a file that doesn't have one of the strings. This one to be exact:
this.parent.Text = xmlDoc.DocumentElement.Sel
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?
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.D ocumentEle ment.Selec tSingleNod e("flags[@ id='prnt'] /@value"), "");
public static string GetValueOrDefault(XmlNode node, string default) {
return node!=null ? node.Value : default;
}
and then:
this.parent.Text = GetValueOrDefault(xmlDoc.D
ASKER
Where do I place
public static string GetValueOrDefault(XmlNode node, string default) {
return node!=null ? node.Value : default;
}
?
public static string GetValueOrDefault(XmlNode node, string default) {
return node!=null ? node.Value : default;
}
?
Well, it's a method, just put it before the method you're using it...
ASKER
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!
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!
for example to get the version.... of the properties...
xtr = new XmlTextReader(openFileDial
xtr.Read();
string s = reader.GetAttribute("versi