LINQ to XML in c# . Enumeration yielded no results? - c#

I'm currently trying to retrieve data from a web service, I would like to do search on results if the score is over 90 for example. I have tried to bring back results without doing searches also and getting no results. Could someone please give me a hand in where I'm going wrong?
FundNamesPayload xmlresponse = new FundNamesPayload();
xmlresponse = search.SearchByName("Australiansuper", "GUID-Here", "Y");
MemoryStream XmlStream = new MemoryStream();
StreamReader XmlReader = new StreamReader(XmlStream);
XmlSerializer Serializer = new XmlSerializer(typeof(FundNamesPayload));
Serializer.Serialize(XmlStream, xmlresponse);
XmlStream.Seek(0, System.IO.SeekOrigin.Begin);
var str = XElement.Parse(XmlReader.ReadToEnd());
var Matching = from data in str.Descendants("FundName")
where(int)data.Element("Score") > 90
select data;
Here is an example of the XML
<SuperFundNamesPayload xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://superfundlookup.gov.au">
<Request>
<Guid>************</Guid>
<Name>HOST Plus</Name>
<ActiveFundsOnly>Y</ActiveFundsOnly>
</Request>
<Response>
<DateTimeRetrieved>2017-09-25T12:20:40.8446457+10:00</DateTimeRetrieved>
<MatchingFundNames>
<NumberOfRecords>2</NumberOfRecords>
<MatchingFundName>
<ABN>
<Value>68657495890</Value>
<IdentifierStatus>Active</IdentifierStatus>
</ABN>
<FundName>
<Name>THE TRUSTEE FOR HOST PLUS SUPERANNUATION FUND</Name>
<NameType>Entity Name</NameType>
<Score>94</Score>
<NameStatus>Current</NameStatus>
</FundName>
<Location>
<StateTerritoryCode>VIC</StateTerritoryCode>
<Postcode>3000</Postcode>
</Location>
</MatchingFundName>
<MatchingFundName>
<ABN>
<Value>80567702967</Value>
<IdentifierStatus>Active</IdentifierStatus>
</ABN>
<FundName>
<Name>The Trustee for HOIST HYDRAULICS VIC P L SUPER FUND</Name>
<NameType>Entity Name</NameType>
<Score>73</Score>
<NameStatus>Current</NameStatus>
</FundName>
<Location>
<StateTerritoryCode>VIC</StateTerritoryCode>
<Postcode>3137</Postcode>
</Location>
</MatchingFundName>
</MatchingFundNames>
</Response>
</SuperFundNamesPayload>

The problem is that the XML document specifies a default namespace:
<SuperFundNamesPayload ... xmlns="http://superfundlookup.gov.au">
So you must specify that namespace when you look up elements:
XNamespace ns = "http://superfundlookup.gov.au";
var Matching = from data in str.Descendants(ns + "FundName")
where (int)data.Element(ns + "Score") > 90
select data;
There a couple atypical features of the LINQ to XML syntax:
You create an XNamespace object by using its implicit string conversion operator instead of new.
You create a namespace-qualified name (an XName) by concatenating an XNamespace object and a string with the + operator.

I had a similar issue because I have forgot to write the constructor (the 'var xmlresponse = new FundNamesPayload();' line). I've got no exception, no error, just an empty (null) result..

Related

Cannot parse xml from Yahoo! Fantasy Sports with c#

I did some searching around the web and could not find the cause of my problem so I apologize if that has already been asked in another form I just did not understand.
My problem is that I am trying to parse the XML retrieved from Yahoo! Fantasy Sports but nothing seems to be working.
I have converted the XML I received (using a GET request with my credentials) into a string. Here it is for evaluation.
<?xml version="1.0" encoding="UTF-8" ?>
- <fantasy_content xml:lang="en-US" yahoo:uri="http://fantasysports.yahooapis.com/fantasy/v2/game/223/players" xmlns:yahoo="http://www.yahooapis.com/v1/base.rng" time="5489.1560077667ms" copyright="Data provided by Yahoo! and STATS, LLC" refresh_rate="60" xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng">
- <game>
<game_key>223</game_key>
<game_id>223</game_id>
<name>Football PLUS</name>
<code>pnfl</code>
<type>full</type>
<url>http://football.fantasysports.yahoo.com/f2</url>
<season>2009</season>
- <players count="25">
- <player>
<player_key>223.p.8261</player_key>
<player_id>8261</player_id>
- <name>
<full>Adrian Peterson</full>
<first>Adrian</first>
<last>Peterson</last>
<ascii_first>Adrian</ascii_first>
<ascii_last>Peterson</ascii_last>
</name>
<editorial_player_key>nfl.p.8261</editorial_player_key>
<editorial_team_key>nfl.t.16</editorial_team_key>
<editorial_team_full_name>Minnesota Vikings</editorial_team_full_name>
<editorial_team_abbr>Min</editorial_team_abbr>
- <bye_weeks>
<week>9</week>
</bye_weeks>
<uniform_number>28</uniform_number>
<display_position>RB</display_position>
- <headshot>
<url>http://l.yimg.com/iu/api/res/1.2/7gLeB7TR77HalMeJv.iDVA--/YXBwaWQ9eXZpZGVvO2NoPTg2MDtjcj0xO2N3PTY1OTtkeD0xO2R5PTE7Zmk9dWxjcm9wO2g9NjA7cT0xMDA7dz00Ng--/http://l.yimg.com/j/assets/i/us/sp/v/nfl/players_l/20120913/8261.jpg</url>
<size>small</size>
</headshot>
<image_url>http://l.yimg.com/iu/api/res/1.2/7gLeB7TR77HalMeJv.iDVA--/YXBwaWQ9eXZpZGVvO2NoPTg2MDtjcj0xO2N3PTY1OTtkeD0xO2R5PTE7Zmk9dWxjcm9wO2g9NjA7cT0xMDA7dz00Ng--/http://l.yimg.com/j/assets/i/us/sp/v/nfl/players_l/20120913/8261.jpg</image_url>
<is_undroppable>1</is_undroppable>
<position_type>O</position_type>
- <eligible_positions>
<position>RB</position>
</eligible_positions>
<has_player_notes>1</has_player_notes>
</player>
- <player>
</players>
</game>
</fantasy_content>
The two methods I have tried are these (PLEASE NOTE: "xmlContent" is the string that contains the XML listed above):
1.)
XDocument chicken = XDocument.Parse(xmlContent);
var menus = from menu in chicken.Descendants("name")
select new
{
ID = menu.Element("name").Value,
};
and
2.)
byte[] encodedString = Encoding.UTF8.GetBytes(xmlContent);
MemoryStream ms = new MemoryStream(encodedString);
XmlDocument doc = new XmlDocument();
doc.Load(ms);
foreach (XmlNode row in doc).SelectNodes("//fantasy_content"))
{}
Basically, I get no results enumerated. I have a feeling I am missing some key steps here though. Any help is greatly appreciated. Thank you all.
UPDATE:
As per the awesome suggestions I received, I tried three more things. Since it is not working still, I did not listen very well. :) Actually, that is semi-accurate, please bear with my newbie attempt at working with XML here as I really am thankful for the responses. Here is how I am screwing up the great suggestions, can you offer another tip on what I missed? Thank you all again.
As per Jon Skeet's suggestion (this yields no results for me):
1.) XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
XDocument chicken = XDocument.Parse(xmlContent);
var menus = from menu in chicken.Descendants(ns + "fantasy_content")
select new
{
ID = menu.Element("name").Value,
};
As per the second suggestion (this throws me an error):
2.) var result = XElement.Load(xmlContent).Descendants().Where(x => x.Name.LocalName == "name");
As per the combinations of suggesting I need to identify the namespace and Yahoo! guide at: http://developer.yahoo.com/dotnet/howto-xml_cs.html
3.) xmlContent = oauth.AcquirePublicData(rtUrl, "GET");
byte[] encodedString = Encoding.UTF8.GetBytes(xmlContent);
MemoryStream ms = new MemoryStream(encodedString);
XmlDocument doc = new XmlDocument();
doc.Load(ms);
XmlNamespaceManager ns = new XmlNamespaceManager(doc.NameTable);
ns.AddNamespace("fantasy_content", "http://fantasysports.yahooapis.com/fantasy/v2/base.rng");
XmlNodeList nodes = doc.SelectNodes("/name", ns);
foreach (XmlNode node in nodes)
{
}
This is what's tripping you up:
xmlns="http://fantasysports.yahooapis.com/fantasy/v2/base.rng"
You're asking for elements in the unnamed namespace - but the elements default to the namespace shown above.
It's easy to fix that in LINQ to XML (and feasible but less simple in XmlDocument)
XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
var menus = chicken.Descendants(ns + "name")
...
Note that in your original method you're actually looking for name elements within the name elements - that's not going to work, but the namespace part is probably enough to get you going.
EDIT: It's not clear why you're using an anonymous type at all, but if you really want all the name element values from the document as ID properties in anonymous type instances, just use:
XNamespace ns = "http://fantasysports.yahooapis.com/fantasy/v2/base.rng";
var menus = chicken.Descendants(ns + "name")
.Select(x => new { ID = x.Value });
Note that there's no need to use Descendants(ns + "fantasy_content") as that's just selecting the root element.
Element name consists of two parts: xmlns(namespace) and localname. If xmlns is absent, name is equal to local name. So, you have to create name with namespace or ignore it
You can ignore namespace in your LINQ, just use LocalName
var result = XElement.Load(#"C:\fantasy_content.xml")
.Descendants()
.Where(x => x.Name.LocalName == "name")
.ToList();

Extract XML from SOAP envelope in c#

I have a response from web service in SOAP envelope as follows:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<ProcessTranResponse xmlns="http://www.polaris.co.uk/XRTEService/2009/03/">
<ProcessTranResult xmlns:a="http://schemas.datacontract.org/2004/07/XRTEService" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:PrintFormFileNameContents i:nil="true"/>
<a:ResponseXML>response_message</a:ResponseXML>
</ProcessTranResult>
</ProcessTranResponse>
</s:Body>
And I want to get response_message to a string variable. I tried doing
XDocument doc = XDocument.Parse(Response);
XNamespace xmlnsa = "http://schemas.datacontract.org/2004/07/XRTEService";
var ResponseXML = doc.Descendants(xmlnsa + "ResponseXML");
And when I use watch I see in ResponseXML -> Results View[0] -> Value my response_message, but I can't figure out what is the next step to get to Value from C#.
XContainer.Descendants returns a collection of elements. You should then try something like this:
foreach (XElement el in ResponseXML)
{
Console.WriteLine(el.Value);
}
Or you can do something like this if you know that there is always only one response:
XDocument doc = XDocument.Parse(Response);
XNamespace xmlnsa = "http://schemas.datacontract.org/2004/07/XRTEService";
XElement ResponseXML = (from xml in XMLDoc.Descendants(xmlnsa + "ResponseXML")
select xml).FirstOrDefault();
string ResponseAsString = ResponseXML.Value;
You can employ several solutions to meet your purpose while you may wanna introduce the structure of xml content or not.
Static attitude
You can simply use this:
XmlDocument _doc = new XmlDocument();
doc.LoadXml(_stream.ReadToEnd());
Then find the desired data with something like this:
doc.LastChild.FirstChild.FirstChild.LastChild.InnerText;
Read xml structure
You can write some additional lines of code to introduce namespaces and other xml contents to find/map the available data, by taking a look at extracting-data-from-a-complex-xml-with-linq

XmlSerializer Deserializing Array/List of Multiple Objects at Root

I'm trying to deserialize the following XML output:
<?xml version="1.0" encoding="ISO-8859-1"?>
<Foo>
<Val>Data1</Val>
</Foo>
<Foo>
<Val>Data2</Val>
</Foo>
(This is output from a hardware device, and cannot be changed)
I have an XML type defined as:
[XmlType(AnonymousType=true, Namespace="")]
public class Foo
{
public string Val { get; set; }
}
I've tried to deserialize this array by creating a serializer like:
var s = new XmlSerializer(typeof(Foo[]));
//or
var s = new XmlSerializer(typeof(List<Foo>);
But every call to s.Deserialize() causes an InvalidOperaitonException:
System.InvalidOperationException: <Foo xmlns=''> was not expected.
Note
var s = new XmlSerializer(typeof(Foo));
// Only deseralizes the first Foo (Data1).
Thanks for your help.
I think the issue is with your provided xml.
Test app says
List<Foo> list = new List<Foo> {new Foo {Val = "Data1"}, new Foo {Val = "Data2"}};
var s = new XmlSerializer(typeof(List<Foo>));
StringBuilder sb = new StringBuilder();
XmlWriter wr = XmlWriter.Create(sb);
s.Serialize(wr, list);
string ss = sb.ToString();
var s2 = new XmlSerializer(typeof(List<Foo>));
StringReader sr = new StringReader(ss);
List<Foo> returnList = (List<Foo>)s2.Deserialize(sr);
And the XML should be
<?xml version="1.0" encoding="utf-16"?>
<ArrayOfFoo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo>
<Val>Data1</Val>
</Foo>
<Foo>
<Val>Data2</Val>
</Foo>
</ArrayOfFoo>
If you can remove the inital line
<?xml version="1.0" encoding="ISO-8859-1"?>
And minipulate the string into
string s = "<ArrayOfFoo><Foo> <Val>Data1</Val></Foo><Foo> <Val>Data2</Val></Foo></ArrayOfFoo>";
var s2 = new XmlSerializer(typeof(List<Foo>));
StringReader sr = new StringReader(s);
List<Foo> list = (List<Foo>)s2.Deserialize(sr);
That could work.
That isn't valid Xml. There needs to be a core root element for it to work properly.
this is not a valid xml so you can not deserialize it like a valid xml. You need some kind of hack to make this work. i'd suggest to insert at beginning of the xml and inserting at the end of xml. then you can deserialize it, since you cant make this change at xml side, do it in your code.
String ss;
// lets assume this holds your xml data in string.
ss.append("</ArrayOfFoo>");
ss.replace("<?xml version=\"1.0\" encoding=\"utf-16\"?>", "<?xml version=\"1.0\" encoding=\"utf-16\"?> <ArrayOfFoo>")
var s2 = new XmlSerializer(typeof(List<Foo>));
StringReader sr = new StringReader(ss);
List<Foo> returnList = (List<Foo>)s2.Deserialize(sr);
now this shall return you the correct list.
As the other posters say, this XML that the hardware device produces is not compatible to the way .NET serializes/deserializes object. Valid XML, and .NET requires valid XML, has a root element.
I suggest:
either you modify your obtained XML to match the way astander presents in his xml code snippet.
or you write a simple xml parser for your file that deserializes the file like you need
br, Marcel
Technically, what you have there is not a well-formed XML document (which has exactly one root element), but rather an well-formed external parsed entity (which can have any number of elements not contained in other elements, as well as text not contained in any elements). Therefore, it should parse if your XML parser has an entry point for parsing EPEs rather than documents. You could also create a stub document which includes by reference your EPE and parse that document.
Xstream for .Net could be a useful API

XDocument Get Part of XML File

I have a large xml file and want to get a defined number of <Cooperation> nodes from it. What's the best way to handle this.
Currently, I'm using this code
public string FullCooperationListChunkGet(int part, int chunksize)
{
StringBuilder output_xml = new StringBuilder();
IEnumerable<XElement> childList = from el in xml.Elements("Cooperations").Skip(part * chunksize).Take(chunksize) select el;
foreach (XElement x in childList.Elements())
{
output_xml.Append(x.ToString());
}
return output_xml.ToString();
}
Skip(part * chunksize).Take(chunksize) doesn't work (seems to be only valid for the Cooperations Tag and not the Cooperation Tags)
Can somebody point me in the right direction.
Thanks,
rAyt
Edit:
The Background is this: I'm pushing these xml parts via a webservice to a Blackberry. Unfortunately, the http request size on a blackberry enterprise server is limited
to 256 kb by default.
Part of the XML File:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Cooperations>
<Cooperation>
<CooperationId>xxx</CooperationId>
<CooperationName>xxx</CooperationName>
<LogicalCustomers>
<LogicalCustomer>
<LogicalCustomerId>xxx</LogicalCustomerId>
<LogicalCustomerName>xxx</LogicalCustomerName>
<Customers>
<Customer>
<CustomerId>xxx</CustomerId>
<CustomerName>xxx/CustomerName>
</Customer>
<Customer>
<CustomerId>xxx</CustomerId>
<CustomerName>xxx</CustomerName>
</Customer>
</Customers>
</LogicalCustomer>
<LogicalCustomer>
<LogicalCustomerId>xxx</LogicalCustomerId>
<LogicalCustomerName>xxx</LogicalCustomerName>
<Customers>
<Customer>
<CustomerId>xxx</CustomerId>
<CustomerName>xxx</CustomerName>
</Customer>
<Customer>
<CustomerId>xxx</CustomerId>
<CustomerName>xxx</CustomerName>
</Customer>
</Customers>
</LogicalCustomer>
<LogicalCustomer>
<LogicalCustomerId>xxx</LogicalCustomerId>
<LogicalCustomerName>xxx</LogicalCustomerName>
<Customers>
<Customer>
<CustomerId>xxx</CustomerId>
<CustomerName>xxx</CustomerName>
</Customer>
</Customers>
</LogicalCustomer>
</LogicalCustomers>
</Cooperation>
<Cooperation>
...
For using XDocument, I expect you want something like:
var qry = doc.Root.Elements("Cooperation").Skip(part*chunksize).Take(chunksize);
however, if the data is large, you might have to drop down to XmlReader instead... I'll try to do an example... (update; 512kb probably isn't worth it...)
The problem with your code is that you are using .Elements() here:
foreach (XElement x in childList.Elements())
{
output_xml.Append(x.ToString());
}
Just remove that:
foreach (XElement x in childList)
{
output_xml.Append(x.ToString());
}
For info - you are also using query syntax unnecessarily:
IEnumerable<XElement> childList = from el in xml.Elements("Cooperations")
.Skip(part * chunksize).Take(chunksize) select el;
is 100% identical to:
IEnumerable<XElement> childList = xml.Elements("Cooperations")
.Skip(part * chunksize).Take(chunksize);
(since the compiler ignores an obvious select, without mapping it to the Select LINQ method)
Do you have an xml document or a fragment, i.e do you have more than 1 "Cooperations" nodes? If you have more, which Coopertation's are you expecting to get? From just 1 Cooperations or across multiple, reason for asking is that you have written xml.Elements("Cooperations").
Wouldn't this do the trick:
xml.Element("Cooperations").Elements("Cooperation").Skip(...).Take(...)
You could do this by using System.Net instead of LINQ, although it would be quite messy. Just to give you an idea on how you can read parts of a http response:
// Get the HTTP response
string url = "http://someurl.com/myxml.xml";
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
// Build a stream
Stream stream = response.GetResponseStream();
Encoding encode = System.Text.Encoding.GetEncoding("utf-8");
StreamReader reader = new StreamReader( stream, encode );
// Loop the file
Char[] read = new Char[256];
int count = reader.Read( read, 0, 256 );
while (count > 0) {
String str = new String(read, 0, count);
count = reader.Read(read, 0, 256);
}
response.Close();
stream.Close();
You can use paging by adjusting the count and simultaneously searching str for XML tags.

Can I avoid having to use fully-qualified element names in LINQ to XML?

Say I call XElement.Parse() with the following XML string:
var xml = XElement.Parse(#"
<?xml version="1.0" encoding="UTF-8"?>
<AccessControlPolicy xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Owner>
<ID>7c75442509c41100b6a413b88b523bd6f46554cdbee5b6cbe27bc08cb3f6a865</ID>
<DisplayName>me</DisplayName>
</Owner>
<AccessControlList>
<Grant>
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Group">
...
");
When it comes time to query the element, I'm forced to use fully-qualified element names because that XML document contains an xmlns attribute in its root. This requires cumbersome creations of XName instances:
var AWS_XMLNS = "http://s3.amazonaws.com/doc/2006-03-01/";
var ownerElement = xml.Element(XName.Get("AccessControlPolicy", AWS_XMLNS)).Element(XName.Get("Owner", AWS_XMLNS));
When what I really want is simply,
var ownerElement = xml.Element("AccessControlPolicy").Element("Owner");
Is there a way to make LINQ to XML assume a specific namespace so I don't have to keep specifying it?
You could simplify by using
XNamespace ns = "http://s3.amazonaws.com/doc/2006-03-01/";
var ownerElement = xml.Element(ns + "AccessControlPolicy").Element(ns + "Owner");
I don't think you can (see Jon Skeet's comment), but there are a few tricks you can do.
1) create an extension method that appends the XNamespace to your string
2) Use VB?!?

Categories