need help using LINQ:XML to create an instance of a class - c#

I have a class which defines meteorological measurements with an API. I have created classes to store these measurements within an XML file. I have been able to write to the XML file, but am unable to read from it. I need to be able to read the data, then instantiate the Measurement Class via the constructor, and then add these instances to a List
Here is the code for the Measurement constructor
public Measurement(string longit, string latitud, string cty, string pcode, DateTime dte, decimal huy, decimal pre, string windDir, int windSp) : base(longit, latitud, cty,pcode)
{
dte = date;
huy = humidity;
pre = precipitation;
windDir = windDirection;
windSp = windSpeed;
}
and here is a sample of the XML file
<Measurement>
<longitude>-76.2858726</longitude>
<latitude>36.8507689</latitude>
<city>Thetford</city>
<postcode>IP24 1ED</postcode>
<date>01/04/2011</date>
<windDirection>SE</windDirection>
<windSpeed>8</windSpeed>
<humidity>2.6</humidity>
<precipitation>0.0</precipitation>
</Measurement>
And here is the method I have written to read the file, and make an instance of the class Measurement. I receive no errors, however have not managed to create the List<>
public List<Measurement> GetList()
{
List<Measurement> measurements = new List<Measurement>();
XDocument doc = XDocument.Load(#"C:\Users\Sonya\Documents\Visual Studio 2010\Projects\WeatherAPI\WeatherAPI\measurement_store.xml");
XElement root = doc.Root;
var d = (from s in root.Descendants("Measurement")
select new Measurement
{
Longitude = s.Element("longitude").Value,
Latitude = s.Element("latitude").Value,
City = s.Element("city").Value,
Postcode = s.Element("postcode").Value,
Date = DateTime.Parse(s.Element("date").Value),
Humidity = decimal.Parse(s.Element("humidity").Value),
Precipitation = decimal.Parse(s.Element("precipitation").Value),
WindSpeed = Convert.ToInt32(s.Element("windSpeed").Value),
WindDirection = s.Element("windDirection").Value,
}).ToList();
d.ForEach(measurements.Add);
return measurements;
}//end GetList()
I've also written another method, of a slightly different format..
public List<Measurement> createXMLListMeasurements()
{
//load the xml document
XDocument doc = XDocument.Load(#"C:\Users\Sonya\Documents\Visual Studio 2010\Projects\WeatherAPI\WeatherAPI\measurement_store.xml");
//XElement root = doc.Root;
//instantiate a new list of measurements
List<Measurement> measurements = new List<Measurement>();
//get a list of measurement elements
var d = from s in doc.Descendants("Measurement")
//where
select new
{ //assign Measurement variables to data from xml doc
Longitude = (string)s.Element("longitude").Value,
Latitude = (string)s.Element("latitude").Value,
City = (string)s.Element("city").Value,
Postcode = (string)s.Element("postcode").Value,
Date = DateTime.Parse(s.Element("date").Value),
Humidity = decimal.Parse(s.Element("humidity").Value),
Precipitation = decimal.Parse(s.Element("precipitation").Value),
WindSpeed = Convert.ToInt32(s.Element("windSpeed").Value),
WindDirection = (string)s.Element("windDirection").Value,
};
foreach (var s in d)
{ //for each element found, instantiate Measurements class, and add to the measurements list.
Measurement m = new Measurement(s.Longitude, s.Latitude, s.City, s.Postcode, s.Date, s.Humidity, s.Precipitation, s.WindDirection, s.WindSpeed);
measurements.Add(m);
}
return measurements;
}
Apologies if these questions seem silly, am VERY new to LINQ and XML, so finding my way very slowly..any help much appreciated! A console application calls this method for testing and produces nothing but
WeatherAPI.Measurement
WeatherAPI.Measurement
Help? thanks!

Overall, your code looks fine. As Jon Skeet pointed out in his comment, you don't need to bother adding each element to a list -- you can simply return the result of the query after calling .ToList().
Most likely, there's either something wrong with your xml file, or you're reading it incorrectly.
If your xml file is truly just:
<Measurement>
<longitude>-76.2858726</longitude>
<latitude>36.8507689</latitude>
<city>Thetford</city>
</Measurement>
Then your code won't work, because the root of the xml file is Measurement. Therefore, calling doc.Root.Descendants("Measurement") will give you 0 results. Instead, your xml file should have a unique root element, for example:
<root>
<Measurement>
<longitude>-76.2858726</longitude>
<latitude>36.8507689</latitude>
<city>Thetford</city>
</Measurement>
<Measurement>
<longitude>-71.2858726</longitude>
<latitude>32.1507689</latitude>
<city>Other City</city>
</Measurement>
</root>
Furthermore, you don't need to bother obtaining the Root of the xml document. If all you want to do is find elements named Measurement, just say doc.Descendants("Measurement").
Try this code with the above xml file:
void Main()
{
var measurements = GetMeasurements(#"C:\path\to\file\measurements.xml");
}
public List<Measurement> GetMeasurements(string path)
{
XDocument doc = XDocument.Load(path);
return (from s in doc.Descendants("Measurement")
select new Measurement
{
Longitude = Convert.ToDecimal(s.Element("longitude").Value),
Latitude = Convert.ToDecimal(s.Element("latitude").Value),
City = s.Element("city").Value,
}).ToList();
}
public class Measurement
{
public decimal Longitude { get; set; }
public decimal Latitude { get; set; }
public string City { get; set; }
}
When I run it in LINQPad, I get the following result:

Related

LINQ to XML query doesn't return any results

I'm trying to query an XML tree and store the resulting element values in a list of objects similar to this example.
The issue is that no matter what I try, I can't populate the list. I don't have any errors, I just have an empty list. I'm almost certain that my query is set up wrong, but I'm new to LINQ and I can't figure out where I've gone wrong. Here's the class I'm using:
public class Individual
{
public string field1 { get; set; }
public string field2 { get; set; }
public bool field3 { get; set; }
}
And here are my vars and query:
XDocument xmlDoc = XDocument.Load(new System.IO.StringReader(MainDataSource.CreateNavigator().OuterXml));
xmlDoc.Descendants()
.Attributes()
.Where(x => x.IsNamespaceDeclaration)
.Remove();
List<Individual> individualList =
(
from el in xmlDoc.Root.Elements("myFields").Descendants("Individual")
select new Individual
{
field1 = (string)el.Element("field1"),
field2 = (string)el.Element("field2"),
field3 = (bool)el.Element("field3")
}
).ToList();
And this is the XML doc:
<?mso-infoPathSolution solutionVersion="1.0.0.31" productVersion="15.0.0" PIVersion="1.0.0.0" href="file:///C:\Users\User\AppData\Local\Microsoft\InfoPath\Designer4\991d50f99c274f7c\manifest.xsf" ?>
<?mso-application progid="InfoPath.Document" versionProgid="InfoPath.Document.4"?>
<myFields xml:lang="en-us" xmlns="http://schemas.microsoft.com/office/infopath/2003/myXSD/2021-02-26T15:29:19">
<DirectReportsList>
<Individual>
<field1>foo</field1>
<field2>bar</field2>
<field3>true</field3>
</Individual>
</DirectReportsList>
<Current_UserID></Current_UserID>
<Current_UserName></Current_UserName>
</myFields>
Your code is looking for a myFields element under the root, but that is the root so nothing is being found.
This what you can do:
XNamespace ns ="http://schemas.microsoft.com/office/infopath/2003/myXSD/2021-02-26T15:29:19";
List<Individual> individualList =
(
from el in xmlDoc.Root.Descendants(ns + "Individual")
select new Individual
{
field1 = (string)el.Element(ns + "field1"),
field2 = (string)el.Element(ns +"field2"),
field3 = (bool)el.Element(ns +"field3")
}
).ToList();
}
If you really don't want to deal with namespaces you can use LocalName of elements but then it is fragile to unexpected xml content.
Update
I just looked at the example you linked to. It doesn't work, the author posted bad code.

SQLServer - Creating well formed XML with For XML

I am trying to generate a large .XML file from the table data in SQL Server. But i am getting the well-formed error when i try to parse it on the .NET side.
SQL Code :-
Select TOP 300000
PID as ID,
Replace(LTRIM(RTRIM(Substring(EFSNameLine1,10,30))),'&','') as NameLine1,
LTRIM(RTRIM(EFSNameLine2)) as NameLine2,
LTRIM(RTRIM(EFSCity)) as City,
LTRIM(RTRIM(EFSState)) as [State],
LTRIM(RTRIM(EFSZip)) as ZipCode,
DateUpdated as DateUpdated
From dbo.partner as Contact with(nolock)
ORDER by PID DESC
FOR XML Auto, Root('CustomerData')
Then i am saving the output from the Sql Server management studio result window to the external XML file. The file size is high and around 60 Meg.
But when i try to parse the XML data into the dataset, i get the exception of not well-formed xml error.
var xmlfilePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "customer-sample-data.xml");
Console.WriteLine("XML File path >>>> " + xmlfilePath);
DataSet objDataSet = new DataSet();
objDataSet.ReadXml(xmlfilePath);
if (objDataSet != null)
{
Console.WriteLine("Data obj is ready !");
}
Please suggest how to produce the large data XML file from the SQL Server Management Studio tool. My requirement is to prepare the sample XML data for the consumption by another application for some testing.
Your code, as posted originally, had a comma in the wrong place... I assume you've modified this for brevity (a good idea). This statement would not work at all (syntax error).
I edited your question to
Select TOP 300000
PID as ID,
Replace(LTRIM(RTRIM(Substring(EFSNameLine1,10,30))),'&','') as NameLine1,
LTRIM(RTRIM(EFSNameLine2)) as NameLine2,
--more columns here
From dbo.partner as Contact with(nolock)
ORDER by PID DESC
FOR XML Auto, Root('CustomerData')
First of all: There is no need to think about the & character (if there is no other reason to replace this)! FOR XML will implicitly escape all forbidden characters:
SELECT 'This <> is & forbidden!' FOR XML PATH('row'),ROOT('test')
Will produce this - completely XML-safe - output:
<test>
<row>This <> is & forbidden!</row>
</test>
And secondly: Any XML generated with FOR XML will be - for sure! - a valid XML. Ohterwise you'd get an exception while running your statement. The error you get is - very probably - not bound to an invaid XML.
Some things with FOR XML AUTO: If your column names might be strange somehow, this can lead to some unexpected element names:
--Name with blank,
--Name with quotes,
--blank only,
--Name with forbidden chararacters,
--Name inlcudes a line break!
DECLARE #tbl TABLE([Strange Name] INT
,[Other'Strange"Name] INT
,[ ] INT
,[Silly<&>] INT
,[With a
Line Break] INT);
INSERT INTO #tbl VALUES(1,1,1,1,1);
SELECT * FROM #tbl; --Working "normally"
SELECT * FROM #tbl FOR XML AUTO, ROOT('test');
Leads to
<test>
<_x0040_tbl Strange_x0020_Name="1" Other_x0027_Strange_x0022_Name="1" _x0020_="1" Silly_x003C__x0026__x003E_="1" With_x0020_a_x0020__x000D__x000A__x0009_Line_x0020_Break="1" />
</test>
The excpetion message points to line 6, Pos 1. Pos 1 might point to a new line in the wrong place. The "ode' might be connected to the ZipCode?
Try to open your XML within an editor and check for errors...
Maybe you are using a variable to small for the whole content somewhere in between the generation and the write-out? This might cut the XML before it ends...
Alltogether I'd advise to use FOR XML PATH() in all cases, with AUTO you never no exactly what happens behind the scenes. PATH allows you to control the output and format much better.
Why do you want to generate it from SQL server and not from code. What you need to do is return your row collection from sql server and then generate xml in your code. Below is the sample code which we use to generate xml from collection of rows.
public static class SerializationHelper
{
public static bool Serialize<T>(T value, out string serializedXml) where T : class
{
serializedXml = string.Empty;
if (value == null)
return false;
try
{
using (var writer = new StringWriter())
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
xmlSerializer.Serialize(writer, value);
serializedXml = writer.ToString();
}
return true;
}
catch (SerializationException)
{
return false;
}
}
public static string ToXml<T>(T value) where T : class
{
string result;
Serialize(value, out result);
return result;
}
public static T FromXml<T>(string xml) where T : class
{
var xmlSerializer = new XmlSerializer(typeof(T));
var reader = new StringReader(xml);
var obj = xmlSerializer.Deserialize(reader);
return obj as T;
}
}
Then you can use same class something like below.
string xml = SerializationHelper.ToXml(items);
For our purpose we are returning generated xml as a string but in your case you case return xml.
Note : Please use xml annotation in your class which you are passing to the above method.
------------------ EDIT : XML from sql and parsing in c# --------------
As per your comments, if you would like to generate it from sql and parse it in c# then you can do something like below.
Here is query which return xml.
select Item1, Item2, Item3 from myTable for Xml RAW('myitem'),elements XSINIL,root('root')
This is how my class will look like for serialization.
[XmlRoot(ElementName = "myitem")]
public class Myitem
{
[XmlElement(ElementName = "Item1")]
public string Item1 { get; set; }
[XmlElement(ElementName = "Item2")]
public string Item2 { get; set; }
[XmlElement(ElementName = "Item3")]
public string Item3 { get; set; }
}
[XmlRoot(ElementName = "root")]
public class Root
{
[XmlElement(ElementName = "myitem")]
public List<Myitem> Myitem { get; set; }
[XmlAttribute(AttributeName = "xsi", Namespace = "http://www.w3.org/2000/xmlns/")]
public string Xsi { get; set; }
}
Then I can call above same helper method like below to generate collection.
var result = await db.GetData();//string xml
var response = SerializationHelper.FromXml<Root>(result);

How to deserialize only part of a large xml file to c# classes?

I've already read some posts and articles on how to deserialize xml but still haven't figured out the way I should write the code to match my needs, so.. I'm apologizing for another question about deserializing xml ))
I have a large (50 MB) xml file which I need to deserialize. I use xsd.exe to get xsd schema of the document and than autogenerate c# classes file which I put into my project. I want to get some (not all) data from this xml file and put it into my sql database.
Here is the hierarchy of the file (simplified, xsd is very large):
public class yml_catalog
{
public yml_catalogShop[] shop { /*realization*/ }
}
public class yml_catalogShop
{
public yml_catalogShopOffersOffer[][] offers { /*realization*/ }
}
public class yml_catalogShopOffersOffer
{
// here goes all the data (properties) I want to obtain ))
}
And here is my code:
first approach:
yml_catalogShopOffersOffer catalog;
var serializer = new XmlSerializer(typeof(yml_catalogShopOffersOffer));
var reader = new StreamReader(#"C:\div_kid.xml");
catalog = (yml_catalogShopOffersOffer) serializer.Deserialize(reader);//exception occures
reader.Close();
I get InvalidOperationException: There is an error in the XML(3,2) document
second approach:
XmlSerializer ser = new XmlSerializer(typeof(yml_catalogShopOffersOffer));
yml_catalogShopOffersOffer result;
using (XmlReader reader = XmlReader.Create(#"C:\div_kid.xml"))
{
result = (yml_catalogShopOffersOffer)ser.Deserialize(reader); // exception occures
}
InvalidOperationException: There is an error in the XML(0,0) document
third: I tried to deserialize the entire file:
XmlSerializer ser = new XmlSerializer(typeof(yml_catalog)); // exception occures
yml_catalog result;
using (XmlReader reader = XmlReader.Create(#"C:\div_kid.xml"))
{
result = (yml_catalog)ser.Deserialize(reader);
}
And I get the following:
error CS0030: The convertion of type "yml_catalogShopOffersOffer[]" into "yml_catalogShopOffersOffer" is not possible.
error CS0029: The implicit convertion of type "yml_catalogShopOffersOffer" into "yml_catalogShopOffersOffer[]" is not possible.
So, how to fix (or overwrite) the code to not get the exceptions?
edits: Also when I write:
XDocument doc = XDocument.Parse(#"C:\div_kid.xml");
The XmlException occures: unpermitted data on root level, string 1, position 1.
Here is the first string of the xml file:
<?xml version="1.0" encoding="windows-1251"?>
edits 2:
The xml file short example:
<?xml version="1.0" encoding="windows-1251"?>
<!DOCTYPE yml_catalog SYSTEM "shops.dtd">
<yml_catalog date="2012-11-01 23:29">
<shop>
<name>OZON.ru</name>
<company>?????? "???????????????? ??????????????"</company>
<url>http://www.ozon.ru/</url>
<currencies>
<currency id="RUR" rate="1" />
</currencies>
<categories>
<category id=""1126233>base category</category>
<category id="1127479" parentId="1126233">bla bla bla</category>
// here goes all the categories
</categories>
<offers>
<offer>
<price></price>
<picture></picture>
</offer>
// other offers
</offers>
</shop>
</yml_catalog>
P.S.
I've already acccepted the answer (it's perfect). But now I need to find "base category" for each Offer using categoryId. The data is hierarchical and the base category is the category that has no "parentId" attribute. So, I wrote a recursive method to find the "base category", but it never finishes. Seems like the algorythm is not very fast))
Here is my code: (in the main() method)
var doc = XDocument.Load(#"C:\div_kid.xml");
var offers = doc.Descendants("shop").Elements("offers").Elements("offer");
foreach (var offer in offers.Take(2))
{
var category = GetCategory(categoryId, doc);
// here goes other code
}
Helper method:
public static string GetCategory(int categoryId, XDocument document)
{
var tempId = categoryId;
var categories = document.Descendants("shop").Elements("categories").Elements("category");
foreach (var category in categories)
{
if (category.Attribute("id").ToString() == categoryId.ToString())
{
if (category.Attributes().Count() == 1)
{
return category.ToString();
}
tempId = Convert.ToInt32(category.Attribute("parentId"));
}
}
return GetCategory(tempId, document);
}
Can I use recursion in such situation? If not, how else can I find the "base category"?
Give LINQ to XML a try. XElement result = XElement.Load(#"C:\div_kid.xml");
Querying in LINQ is brilliant but admittedly a little weird at the start. You select nodes from the Document in a SQL like syntax, or using lambda expressions. Then create anonymous objects (or use existing classes) containing the data you are interested in.
Best is to see it in action.
miscellaneous examples of LINQ to XML
simple sample using xquery and lambdas
sample denoting namespaces
There is tons more on msdn. Search for LINQ to XML.
Based on your sample XML and code, here's a specific example:
var element = XElement.Load(#"C:\div_kid.xml");
var shopsQuery =
from shop in element.Descendants("shop")
select new
{
Name = (string) shop.Descendants("name").FirstOrDefault(),
Company = (string) shop.Descendants("company").FirstOrDefault(),
Categories =
from category in shop.Descendants("category")
select new {
Id = category.Attribute("id").Value,
Parent = category.Attribute("parentId").Value,
Name = category.Value
},
Offers =
from offer in shop.Descendants("offer")
select new {
Price = (string) offer.Descendants("price").FirstOrDefault(),
Picture = (string) offer.Descendants("picture").FirstOrDefault()
}
};
foreach (var shop in shopsQuery){
Console.WriteLine(shop.Name);
Console.WriteLine(shop.Company);
foreach (var category in shop.Categories)
{
Console.WriteLine(category.Name);
Console.WriteLine(category.Id);
}
foreach (var offer in shop.Offers)
{
Console.WriteLine(offer.Price);
Console.WriteLine(offer.Picture);
}
}
As an extra: Here's how to deserialize the tree of categories from the flat category elements.
You need a proper class to house them, for the list of Children must have a type:
class Category
{
public int Id { get; set; }
public int? ParentId { get; set; }
public List<Category> Children { get; set; }
public IEnumerable<Category> Descendants {
get
{
return (from child in Children
select child.Descendants).SelectMany(x => x).
Concat(new Category[] { this });
}
}
}
To create a list containing all distinct categories in the document:
var categories = (from category in element.Descendants("category")
orderby int.Parse( category.Attribute("id").Value )
select new Category()
{
Id = int.Parse(category.Attribute("id").Value),
ParentId = category.Attribute("parentId") == null ?
null as int? : int.Parse(category.Attribute("parentId").Value),
Children = new List<Category>()
}).Distinct().ToList();
Then organize them into a tree (Heavily borrowed from flat list to hierarchy):
var lookup = categories.ToLookup(cat => cat.ParentId);
foreach (var category in categories)
{
category.Children = lookup[category.Id].ToList();
}
var rootCategories = lookup[null].ToList();
To find the root which contains theCategory:
var root = (from cat in rootCategories
where cat.Descendants.Contains(theCategory)
select cat).FirstOrDefault();

Deserialization : several kind of nodes into the same object

I'm trying to deserialize an XML document, one of its nodes can be represented like this :
<n1 zone="00000" id="0000" />
or this :
<n2 zone="00000" id="0000" />
or this :
<n3 zone="00000" id="0000" />
In my document I will always have one "n1" node or one "n2" node or one "n3" node. I'd like to deserialize all these fragments into an instance of this class :
[Serializable]
public class N
{
[XmlAttribute("zone")]
public string Zone { get; set; }
[XmlAttribute("id")]
public string Id { get; set; }
}
But I didn't manage to do that. The documentation suggests to use the XmlChoiceIdentifier attribute in order to accomplish this, but maybe I used it in a wrong way.
Any idea ?
PS : I know I can create three classes : N1, N2 and N3, and map them to my different types of XML fragments. But I'd prefer a cleaner solution.
Assuming you have something like:
<?xml version="1.0"?>
<ns>
<n1 id="0000" zone="00000"/>
<n2 id="0000" zone="00000"/>
<n3 id="0000" zone="00000"/>
</ns>
You could use LINQ to XML:
XDocument document = XDocument.Load(path);
XElement parentNode = document.Element("ns");
var childNodes = parentNode.Elements().Where(x => x.Name.ToString().StartsWith("n")); //this prevent from take elements wich didn't start with n
List<N> list = new List<N>();
foreach (XElement element in childNodes) {
N n = new N(){
Id = element.Attribute("id").Value,
Zone = element.Attribute("zone").Value
};
list.Add(n);
}
Here is a fairly understandable read on how to use XmlChoiceIdentifier:
http://msdn.microsoft.com/en-us/magazine/cc164135.aspx
If you are really having trouble with this, then you could always use an XSL transform to do the mapping first.
You could use LINQ-To-Xml
XDocument x = XDocument.Parse(
"<root><n1 zone=\"0000\" id=\"0000\"/><n2 zone=\"0001\" id=\"0011\"/><n3 zone=\"0002\" id=\"0022\"/></root>");
var result = from c in x.Element("root").Descendants()
select new N { Zone = c.Attribute("zone").Value,
Id = c.Attribute("id").Value };
It's not using a serializer, which I think you are aiming for but it's a way forward.

Need help parsing XML feed in Windows Phone

I have an XML feed that I've retrieved via httpwebrequest, and I'm having problems parsing the feed since its unlike the times I've tried this in the past. So far I have the url
http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni&r=N
which I've stored in
XDocument doc = XDocument.Parse(feedString);
And I know that when I've dumped that all in a listbox for debugging purposes, I get everything there, I'm just having problems parsing the feed:
<body copyright="All data copyright San Francisco Muni 2012.">
<route tag="N" title="N-Judah" color="003399" oppositeColor="ffffff" latMin="37.7601699" latMax="37.7932299" lonMin="-122.5092" lonMax="-122.38798">
<stop tag="5240" title="King St & 4th St" lat="37.7760599" lon="-122.39436" stopId="15240"/>
<stop tag="5237" title="King St & 2nd St" lat="37.7796199" lon="-122.38982" stopId="15237"/>
<stop tag="7145" title="The Embarcadero & Brannan St" lat="37.7846299" lon="-122.38798" stopId="17145"/>
<stop tag="4510" title="Embarcadero Folsom St" lat="37.7907499" lon="-122.3898399" stopId="14510"/>
<stop tag="5629" title="Tunnel Entry Point Inbound Nea" lat="37.79279" lon="-122.39126" stopId="15629"/>
And so on and so on
I'd like to store each attribute in each stop tag into an array, but I'm completely stumped as to how I'd begin even.
Thanks
Update:
I think I got it to work on that first msdn link, but this only gets the first line:
using (XmlReader reader = XmlReader.Create(new StringReader(feed)))
{
reader.ReadToFollowing("stop");
reader.MoveToFirstAttribute();
string tag = reader.Value;
reader.MoveToNextAttribute();
string title = reader.Value;
reader.MoveToNextAttribute();
string lat = reader.Value;
reader.MoveToNextAttribute();
string lon = reader.Value;
}
How would I loop through each stop with that code above?
Thanks
EDIT: #2
This loop works, but keeps showing the first row of stop attributes:
using (XmlReader reader = XmlReader.Create(new StringReader(feed)))
{
reader.ReadToFollowing("stop");
while (reader.MoveToNextAttribute())
{
// Move the reader back to the element node.
//reader.ReadToFollowing("stop");
reader.MoveToFirstAttribute();
string tag = reader.Value;
MessageBox.Show(tag);
reader.MoveToNextAttribute();
string title = reader.Value;
MessageBox.Show(title);
reader.MoveToNextAttribute();
string lat = reader.Value;
MessageBox.Show(lat);
reader.MoveToNextAttribute();
string lon = reader.Value;
MessageBox.Show(lon);
}
reader.MoveToElement();
}
I feel like I'm so close to figuring it out.
Here is a complete solution with the help of Linq. Just see what result contains
using (WebClient w = new WebClient())
{
string xml = w.DownloadString("http://webservices.nextbus.com/service/publicXMLFeed?command=routeConfig&a=sf-muni&r=N");
XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Descendants("stop")
.Select(n => new
{
Tag = (string)n.Attribute("tag"),
Title = (string)n.Attribute("title"),
Lat = (string)n.Attribute("lat"),
Lon = (string)n.Attribute("lon"),
StopId = (string)n.Attribute("stopId")
})
.ToArray();
}
Here is something you can use:
XmlReader xmlReader = XmlReader.Create("http://webservices.nextbus.com/service/publicXMLFeed? command=routeConfig&a=sf-muni&r=N");
List<string> aTitle= new List<string>();
// Add as many as attributes you have in your "stop" element
while (xmlReader.Read())
{
//keep reading until we see your element
if (xmlReader.Name.Equals("stop") && (xmlReader.NodeType == XmlNodeType.Element))
{
string title = xmlReader.GetAttribute("title");
aTitle.Add(title);
// Add code for all other attribute you would want to store in list.
}
}
Finally call the list and based on index you can get all the items.
There are many way to parse XML file. Here is one simple approach:
XDocument doc = XDocument.Load(feedString);
var stops = doc.Document.Descendants(XName.Get("route"));
// Loop over all stops in the XML
foreach (var stop in stops)
{
}
I'm not sure what do you mean by "store each attribute in each stop tag into an array", but I would define a type:
class RouteStop
{ // make setters private and init them in ctor to make it immutable
public string Tag {get; set;} //maybe int ?
public string Title {get; set;}
public double Latitude {get; set;}
public double Longitude {get; set;}
public int ID {get; set;}
}
Then define a List of RouteStop
List<RouteStop> routeStops = new List<RouteStop>();
and simply in the create object in foreach loop
foreach (var stop in stops)
{
var tag = stop.Attribute("tag").Value;
var title = stop.Attribute("title").Value;
var long = double.Parse(stop.Attribute("lon").Value,
CultureInfo.InvariantCulture);
//etc
routeStops.add(new RouteStop() { Tag = tag } //and so on
}

Categories