4
\$\begingroup\$

This is the way I'm currently getting data from XML, but it seems to be really inefficient, checking every localname in every iteration. How should I be doing it?

Here is a sample of the XML I am trying to parse, followed by my code.

<?xml version="1.0" encoding="UTF-8"?>
<hash>
 <result>
 <properties type="array">
 <property>
 <last-account-review-at>2011年04月26日 00:00:04</last-account-review-at>
 <unit-balance type="integer">-104</unit-balance>
 <daily-consumption type="decimal">10.8</daily-consumption>
 <status>active</status>
 <end-date></end-date>
 <icp-number>0001234567RN602</icp-number>
 <address>
 <property-name nil="true"></property-name>
 <flat-number>3</flat-number>
 <suburb>Suburbia</suburb>
 <street-number>21</street-number>
 <region>Nether</region>
 <street-name>Easy</street-name>
 <district>Metropolis</district>
 </address>
 <start-date>2010年05月05日</start-date>
 <status-detail nil="true"></status-detail>
 </property>
 </properties>
 <account-number>9001234567</account-number>
 </result>
 <version>1.0</version>
</hash>

I don't have any issues with the maintainability of my code, it's more that it seems like I shouldn't be comparing against so many things at every iteration.

public static class DataRetrieval
 {
 private static XmlNameTable _nt;
 private static object _propertyElement;
 private static object _unitBalanceElement;
 private static object _dailyConsumptionElement;
 private static object _statusElement;
 static DataRetrieval()
 {
 _nt = new NameTable();
 _propertyElement = _nt.Add("property");
 _unitBalanceElement = _nt.Add("unit-balance");
 _dailyConsumptionElement = _nt.Add("daily-consumption");
 _statusElement = _nt.Add("status");
 } 
 public static bool ParseCustomerData(string raw, out List<PropertyDetails> properties)
 {
 bool parsedOK = false;
 properties = new List<PropertyDetails>();
 PropertyDetails property = null;
 XmlReaderSettings settings = new XmlReaderSettings();
 settings.IgnoreComments = true;
 settings.IgnoreWhitespace = true;
 settings.NameTable = _nt;
 XmlReader reader = XmlReader.Create(new StringReader(raw), settings);
 object localname;
 bool continueReading = true;
 int dataItemsRead = 0;
 const int DataItemsToRead = 3;
 try
 {
 while (continueReading)
 {
 if (reader.IsStartElement())
 {
 localname = reader.LocalName;
 if (localname == _propertyElement)
 {
 if (property != null)
 {
 if (dataItemsRead == DataItemsToRead)
 {
 properties.Add(property);
 dataItemsRead = 0;
 }
 else
 {
 break;
 }
 }
 property = new PropertyDetails();
 continueReading = reader.Read();
 }
 else if (localname == _unitBalanceElement)
 {
 property.UnitBalance = reader.ReadElementContentAsInt();
 dataItemsRead++;
 }
 else if (localname == _dailyConsumptionElement)
 {
 property.DailyConsumption = reader.ReadElementContentAsDouble();
 dataItemsRead++;
 }
 else if (localname == _statusElement)
 {
 if (property.SetStatus(reader.ReadElementContentAsString()))
 {
 dataItemsRead++;
 }
 }
 else
 {
 continueReading = reader.Read();
 }
 }
 else
 {
 continueReading = reader.Read();
 }
 }
 }
 catch (XmlException e)
 {
 Debug.WriteLine("XmlException: {0}", e.Message);
 }
 parsedOK = dataItemsRead == DataItemsToRead;
 if ((property != null) && (parsedOK))
 {
 properties.Add(property);
 }
 return parsedOK;
 }
 }
asked Jun 5, 2011 at 23:52
\$\endgroup\$

3 Answers 3

8
\$\begingroup\$

Have a look at

Linq to XML (MSDN)

The venerable Scott Gu has a nice piece of Linq to XML here. That should help get you started!

Using Linq to XML should definately make your code easier to read and maintain.

Michael K
2,9091 gold badge22 silver badges24 bronze badges
answered Jun 6, 2011 at 1:48
\$\endgroup\$
8
\$\begingroup\$

You could try using XDocument instead.

It would also help to see an example of the xml you're trying to parse in order to properly suggest a refactoring of your code.

EDIT:
You could do this (XDocument + LINQ):

string sXml = @"<?xml version=""1.0"" encoding=""UTF-8""?>
 <hash>
 <result>
 <properties type=""array"">
 <property>
 <last-account-review-at>2011年04月26日 00:00:04</last-account-review-at>
 <unit-balance type=""integer"">-104</unit-balance>
 <daily-consumption type=""decimal"">10.8</daily-consumption>
 <status>active</status>
 <end-date></end-date>
 <icp-number>0001234567RN602</icp-number>
 <address>
 <property-name nil=""true""></property-name>
 <flat-number>3</flat-number>
 <suburb>Suburbia</suburb>
 <street-number>21</street-number>
 <region>Nether</region>
 <street-name>Easy</street-name>
 <district>Metropolis</district>
 </address>
 <start-date>2010年05月05日</start-date>
 <status-detail nil=""true""></status-detail>
 </property>
 </properties>
 <account-number>9001234567</account-number>
 </result>
 <version>1.0</version>
 </hash>";
var xml = XDocument.Parse(sXml);
var r = from x in xml.Descendants("property")
 select new 
 {
 unitBalance = x.Element("unit-balance").Value,
 dailyConsumption = x.Element("daily-consumption").Value,
 status = x.Element("status").Value
 };
this._propertyElement = xml.Descendants("property");
this._unitBalanceElement = r.ElementAt(0).unitBalance;
this._dailyConsumptionElement = r.ElementAt(0).dailyConsumption;
this._statusElement = r.ElementAt(0).status;

Note: I didn't include any error checking, just wanted to show some possibilities.

answered Jun 6, 2011 at 0:34
\$\endgroup\$
1
  • \$\begingroup\$ Personally i love XDocument with linq it provides cleaner and better options. \$\endgroup\$ Commented Jun 10, 2011 at 9:16
0
\$\begingroup\$

If your'e dealing with a given scheme definition in .xsd you can use the xsd.exe tool to generate matching classes. Using those generated classes provides type safety. Here you can find an easy example.

If you don't like xsd.exe xsd2code is another alternative for code generation.

answered Jun 6, 2011 at 15:30
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.