Needing C# LINQ Help reading XML - c#

I've got xml that looks like what I have below. I can read the title but am having trouble getting to the url of media:content. Any suggestions? My non-working c# is below along with the xml.
XNamespace xmlns = "http://www.w3.org/2005/Atom";
var names =
(from data in XDocument.Load("http://channel9.msdn.com/Events/Build/2012/rss").Descendants("item")
let xElement = data.Element("title")
let xElementUrls = data.Element("media")
where xElement != null
select new
{
Title = xElement.Value,
Urls = data.Elements(xmlns + "media:group")
//MediaGroup = data.Element("media:group")
}).ToList();
and the XML:
<?xml-stylesheet type="text/xsl" media="screen" href="/styles/xslt/rss.xslt"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
xmlns:media="http://search.yahoo.com/mrss/"
xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd"
xmlns:c9="http://channel9.msdn.com">
<channel>
<item>
<title>Building Windows 8 LOB Apps (Repeat)</title>
<media:group>
<media:content url="http://video.ch9.ms/sessions/build/2012/2-104R.mp4"
expression="full" duration="0" fileSize="1" type="video/mp4" medium="video"/>
<media:content url="http://video.ch9.ms/sessions/build/2012/2-104R.wmv"
expression="full" duration="0" fileSize="1" type="video/x-ms-wmv" medium="video"/>
</media:group>
</item>
<item>
<item>
...
</item>
Added this based on L.B.'s suggestion but I can't figure out how to get url's out (it is now returning a list of URL's per item as expected.
var items = xDoc.Descendants("item")
.Where(g => g.Element(media + "group") != null)
.Select(g => new {
Title = g.Element("title").Value,
Url = g.Element(media + "group")
.Element(media + "content")
.Attribute("url").Value
})
.ToList();

var xDoc = XDocument.Load("http://channel9.msdn.com/Events/Build/2012/rss");
XNamespace media = "http://search.yahoo.com/mrss/";
var items = xDoc.Descendants("item")
.Where(g => g.Element(media + "group") != null)
.Select(g => new {
Title = g.Element("title").Value,
Url = g.Element(media + "group")
.Element(media + "content")
.Attribute("url").Value
})
.ToList();

media is a namespace alias, not an element.
You need to get the group element within the http://search.yahoo.com/mrss/ namespace:
XNamespace m = "http://search.yahoo.com/mrss/";
let xElementUrls = data.Element(m + "group")

You have the wrong namespace...
XNamespace xmlns = "http://www.w3.org/2005/Atom";
should be
XNamespace xmlns = "http://search.yahoo.com/mrss/";
And you are combining the namespace and element name incorrectly
...
Urls = data.Elements(xmlns + "group")
....

Related

Change Value of nested node

This seems like a simple question but I can't seem to get started on a working solution. The final goal is to change the value of the ConstantValue element highlighted below. My strategy is to find the Component node and drill down from there. The problem is that keep returning a null and I'm not sure why. Below is the code I'm using a the xml I'm using. Any hints would be great.
XDocument xmlDoc = XDocument.Parse(str);
var items = xmlDoc.Descendants("Component")
.Where(x => x.Attribute("Name").Value == "axesInterface")
.FirstOrDefault();
<?xml version="1.0" encoding="utf-8"?>
<Document>
<Engineering version="V17" />
<DocumentInfo>
</DocumentInfo>
<SW.Blocks.FB ID="0">
<AttributeList>
<Interface><Sections></Sections></Interface>
<MemoryLayout>Optimized</MemoryLayout>
<MemoryReserve>100</MemoryReserve>
<Name>EM00_CM01_Warp1</Name>
<Number>31650</Number>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
<SetENOAutomatically>false</SetENOAutomatically>
</AttributeList>
<ObjectList>
<SW.Blocks.CompileUnit ID="4" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource>
<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4">
<Parts>
<Access Scope="GlobalVariable" UId="27">
<Symbol>
<Component Name="HMIAxisCtrl_Interface" />
<Component Name="axesInterface" AccessModifier="Array">
<Access Scope="LiteralConstant">
<Constant>
<ConstantType>DInt</ConstantType>
<ConstantValue>0</ConstantValue>
</Constant>
</Access>
</Component>
</Symbol>
</Access>
</Parts>
</FlgNet>
</NetworkSource>
</AttributeList>
</SW.Blocks.CompileUnit>
</ObjectList>
</SW.Blocks.FB>
</Document>
You can use XQuery to get the correct node:
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
//
var nm = new XmlNamespaceManager(new NameTable());
nm.AddNamespace("sm", "http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v4");
var node = xdoc.XPathSelectElement(#"//sm:Access[#Scope=""LiteralConstant""]/sm:Constant/sm:ConstantValue", nm);
node.Value = "Something Else";
dotnetfiddle
For multiple nodes, change XPathSelectElement to XPathSelectElements
On your XML example, you can simply get specified node with this piece of code at any depth:
XDocument xmlDoc = XDocument.Parse(str);
XElement node = xmlDoc.Descendants().FirstOrDefault(x => x.Name.LocalName == "ConstantValue");
node.Value = "New value";
xmlDoc.Save(somewhere);
If there may be multiple nodes with "ConstantValue" name - then just replace FirstOrDefault with Where and work with filtered by names IEnumerable<XElement>.
Why it isn't find by x.Name == "ConstantValue":
EDIT: added samples.
// That's your parent node
var componentNode = xmlDoc.Descendants()
.Where(x => x.Name.LocalName == "Component"
&& (string)x.Attribute("Name") == "axesInterface");
// Get first ConstantValue node from parent Component node
var constantValueNode = componentNode.Descendants()
.FirstOrDefault(x => x.Name.LocalName == "ConstantValue");
// or get all ConstantValue nodes from parent Component node
var constantValueNodes = componentNode.Descendants()
.Where(x => x.Name.LocalName == "ConstantValue");
if (constantValueNode is XElement)
constantValueNode.Value = "New value";
You can create extension method to get parent node by specifying level:
public static class Extensions
{
public static XElement GetParentNode(this XElement element, int parentLevel)
{
XElement node = element.Parent;
for (int i = 0; i < parentLevel; i++)
node = node.Parent;
return node;
}
}
In your example, var parent = constantValueNode.GetParentNode(2); will return Component node (with attribute Name="axesInterface"). var parent = constantValueNode.GetParentNode(12); will return root "Document" node.

Linq to XML elements

Hi I try to read an xml file with LINQ.
The name of the file is:categorizedBooks.xml
and the content of the file looks like this:
<category name=".NET">
<books>
<book>CLR via C#</book>
<book>Essential .NET</book>
</books>
</category>
The code for reading the file looks like this:
XElement rootss = XElement.Load(#"D:/categorizedBooks.xml");
XElement dotNetCategoryss = rootss.Element("category");
XAttribute namehallo = dotNetCategoryss.Attribute("name");
XElement booksss = dotNetCategoryss.Element("books");
IEnumerable<XElement> bookElements = booksss.Elements("book");
Console.WriteLine((string)dotNetCategoryss);
foreach (XElement bookElement in bookElements)
{
Console.WriteLine(" - " + (string)bookElement);
}
But i get null on this line:
XAttribute namehallo = dotNetCategoryss.Attribute("name");
So how to fix this , so that it not will be null?
Thank you
Your doc root is the category element - try:
XAttribute namehallo = rootss.Attribute("name");
Console.WriteLine(namehallo.Value);
You'll have to change your other XElement references similarly
instead of XElement.Load use XDocument.Load or XDocument.Parse
var xml = #"
<category name='.NET'>
<books>
<book>CLR via C#</book>
<book>Essential .NET</book>
</books>
</category>";
var document = XDocument.Parse(xml);
// returns: .NET
var category = document
.Element("category")
.Attributes("name")
.Select(p => p.Value);
// returns: CLR via C#, Essential .NET
var books = document
.Descendants("book")
.Select(p => p.Value);

Using Linq to return a list of attributes in a node

I am attempting to write a LINQ query to pull `ows_Alert="This is the text for an alert" into a list. there may be more than 1 "ows_Alert" so what I'm trying to do is retrieve a list of alerts, and place them as items of a split button drop-down menu.
I tried, attributes, and I tried elements, but htey are neither? How do I retrieve the ows_Alert value?
My code is as follows:
pAlerts= xDocument.Elements().ToList();
pAlerts.ForEach(item => tsSplitBtnAlerts.DropDownItems
.Add(item.Attribute("ows_Alert").ToString()));
The XML File
<?xml version="1.0" encoding="utf-8" ?>
<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema" xmlns="http://schemas.microsoft.com/sharepoint/soap/">
<rs:data ItemCount="1">
<z:row ows_ContentTypeId="0x0100D2769100E1771B4A94C86F01916A3C4F"
ows_Title="Alert Test"
ows_Alert="This is the text for an alert"
ows_ID="1" ows_ContentType="Item"
ows_Modified="2013-08-02 11:19:07" ows_Created="2013-08-02 11:19:07" ows_Author="1;#REDACTED" ows_Editor="1;#REDACTED"
ows_owshiddenversion="1" ows_WorkflowVersion="1" ows__UIVersion="512" ows__UIVersionString="1.0"
ows_Attachments="0" ows__ModerationStatus="0" ows_LinkTitleNoMenu="Alert Test" ows_LinkTitle="Alert Test"
ows_LinkTitle2="Alert Test" ows_SelectTitle="1" ows_Order="100.000000000000" ows_GUID="{77BD9162-461F-4A97-89E3-033E387E76A9}"
ows_FileRef="1;#Lists/PortalToolbarAlerts/1_.000" ows_FileDirRef="1;#Lists/PortalToolbarAlerts"
ows_Last_x0020_Modified="1;#2013-08-02 11:19:07" ows_Created_x0020_Date="1;#2013-08-02 11:19:07" ows_FSObjType="1;#0"
ows_SortBehavior="1;#0" ows_PermMask="0xb008431061" ows_FileLeafRef="1;#1_.000" ows_UniqueId="1;#{3E3EA8F8-16B2-4DD0-81B1-BAA9592302E9}"
ows_ProgId="1;#" ows_ScopeId="1;#{4310D927-E486-4B8C-8034-52937AC5A6D8}" ows__EditMenuTableStart="1_.000" ows__EditMenuTableStart2="1"
ows__EditMenuTableEnd="1" ows_LinkFilenameNoMenu="1_.000" ows_LinkFilename="1_.000" ows_LinkFilename2="1_.000"
ows_ServerUrl="/Lists/PortalToolbarAlerts/1_.000" ows_EncodedAbsUrl="http://REDACTEDSITE/1_.000"
ows_BaseName="1_" ows_MetaInfo="1;#" ows__Level="1" ows__IsCurrentVersion="1" ows_ItemChildCount="1;#0" ows_FolderChildCount="1;#0" />
</rs:data>
</listitems>
XNamespace z = "#RowsetSchema";
var alerts = xDocument.Descendants(z + "row")
.Select(row => (string)row.Attribute("ows_Alert"))
.ToList();
var ns = XNamespace.Get("#RowsetSchema");
var alerts = xml
.Descendants(ns + "row")
.Select(row => row.Attribute("ows_Alert").Value);
You have to include the namespaces when traversing the document. I tried this when using your xml in "test.xml" and it worked.
XDocument document = XDocument.Load(Server.MapPath("~/test.xml"), LoadOptions.None);
XNamespace ns = "urn:schemas-microsoft-com:rowset";
XNamespace z = "#RowsetSchema";
IEnumerable<XElement> datas = document.Root.Elements(ns + "data");
foreach (XElement data in datas)
{
IEnumerable<XElement> rows = data.Elements(z + "row");
string alerts = "";
foreach (XElement row in rows)
{
alerts += row.Attribute("ows_Alert").Value + "<br />";
}
Test.Text = alerts;
}

parse xml - get youtube description from videos in playlist

Im trying to get the description of a youtube video from a youtube playlist.
the xml playlist looks something like this:
<entry gd:etag="W/"YhjhqeyM."">
<title>My Title</title>
<media:group>
<media:description type="plain">My description</media:description>
</media:group>
</entry>
.
.
.
...multiple entry elements
I'm looping through the multiple entries and getting the title like so:
var x = (from e in xmlFeed.Root.Elements().Where(n => n.Name.LocalName == "entry") select e);
foreach (XElement element in x)
{
title = element.Elements().Where(n => n.Name.LocalName == "title").FirstOrDefault().Value;
description = //??;
}
How can i get the description given the above format?
Thanks
description = element.Descendants(XName.Get("description")).First().Value();
This does it, it's up to you how you're loading in your doc though. I've read it in with a HttpWebRequest/StreamReader in this case:
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc.Load(requestData("https://gdata.youtube.com/feeds/api/playlists/63F0C78739B09958?v=2"));
var node = doc.DocumentNode.Descendants("media:description").ToList();
foreach (var description in node)
{
Response.Write(string.Format("Description: {0}\n",description.InnerText.ToString()));
}
XDocument xDoc = XDocument.Load("https://gdata.youtube.com/feeds/api/playlists/63F0C78739B09958?v=2");
XNamespace media = XNamespace.Get("http://search.yahoo.com/mrss/");
var descs = xDoc.Descendants(media + "description")
.Select(d => d.Value)
.ToArray();

Problems working with LINQ to XML

I'm trying to work with LINQ to XML to parse the notifications I'm getting from Google Checkout.
The response is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<authorization-amount-notification xmlns="http://checkout.google.com/schema/2" serial-number="153286076708098-00005-6">
<authorization-amount currency="USD">60.0</authorization-amount>
<authorization-expiration-date>2011-07-03T21:27:48.000Z</authorization-expiration-date>
<avs-response>Y</avs-response>
<cvn-response>M</cvn-response>
<timestamp>2011-06-26T21:28:48.741Z</timestamp>
<google-order-number>153286076708098</google-order-number>
<order-summary>
<total-chargeback-amount currency="USD">0.0</total-chargeback-amount>
<google-order-number>153286076708098</google-order-number>
<total-charge-amount currency="USD">0.0</total-charge-amount>
<total-refund-amount currency="USD">0.0</total-refund-amount>
<risk-information>
<ip-address>77.42.229.34</ip-address>
<billing-address>
<address1>somewhere in Beirut</address1>
<address2></address2>
<phone>70892555</phone>
<email>Technical#fisharwe.com</email>
<contact-name>Fisharwe User</contact-name>
<company-name></company-name>
<fax></fax>
<country-code>LB</country-code>
<city>Beirut</city>
<region></region>
<postal-code>1000</postal-code>
</billing-address>
<avs-response>Y</avs-response>
<cvn-response>M</cvn-response>
<eligible-for-protection>true</eligible-for-protection>
<partial-cc-number>1111</partial-cc-number>
<buyer-account-age>18</buyer-account-age>
</risk-information>
<authorization>
<authorization-amount currency="USD">60.0</authorization-amount>
<authorization-expiration-date>2011-07-03T21:27:48.000Z</authorization-expiration-date>
</authorization>
<purchase-date>2011-06-26T21:27:48.000Z</purchase-date>
<archived>false</archived>
<shopping-cart>
<items>
<item>
<item-name>Credits</item-name>
<item-description>Description</item-description>
<unit-price currency="USD">60.0</unit-price>
<quantity>1</quantity>
</item>
</items>
</shopping-cart>
<order-adjustment>
<merchant-codes />
<total-tax currency="USD">0.0</total-tax>
<adjustment-total currency="USD">0.0</adjustment-total>
</order-adjustment>
<promotions />
<buyer-id>975104325298289</buyer-id>
<buyer-marketing-preferences>
<email-allowed>false</email-allowed>
</buyer-marketing-preferences>
<buyer-shipping-address>
<address1>somewhere in Beirut</address1>
<address2></address2>
<phone>70892555</phone>
<email>Technical#fisharwe.com</email>
<contact-name>Fisharwe User</contact-name>
<company-name></company-name>
<fax></fax>
<structured-name>
<first-name>Fisharwe</first-name>
<last-name>User</last-name>
</structured-name>
<country-code>LB</country-code>
<city>Beirut</city>
<region></region>
<postal-code>1000</postal-code>
</buyer-shipping-address>
<order-total currency="USD">60.0</order-total>
<fulfillment-order-state>NEW</fulfillment-order-state>
<financial-order-state>CHARGEABLE</financial-order-state>
</order-summary>
</authorization-amount-notification>
Here's the code I'm using:
var serverResponse = _checkoutService.Post(data, GoogleCheckoutConstants.ReportsUri);
var xmlData = XDocument.Parse(serverResponse);
bool charged = false;
if(xmlData.Root.Name.Equals("authorization-amount-notification"))
{
var amount = (from c in xmlData.Elements()
where c.Name.Equals("authorization-amount")
select c).First().Value;
var googleNumber = (from c in xmlData.Elements()
where c.Name.Equals("google-order-number")
select c).First().Value;
_checkoutService.ChargeAndShip(googleNumber, amount);
charged = true;
}
This is the first time I use LINQ to XML, so I'm not really sure what's wrong with my code. But it's not even going inside the if statement. So when I replace the condition with:
if (serverResponse.IndexOf("authorization-amount-notification") > -1)
I end up getting errors telling me that the amount and googleNumber were not found.
Any suggestions?
You need to put the namespace in to the Xml, and you the Elements are SubElements of the Root Node.
You are only after one Element so doing Elements() then .First() is pointless. Just do Element() instead.
Also, you can match element names by passing in the Name of the Element + namespace to the Element() method.
var xmlData = XDocument.Parse(xml);
XNamespace ns = "http://checkout.google.com/schema/2";
if (xmlData.Root.Name == ns + "authorization-amount-notification")
{
var amount =
xmlData
.Root
.Element(ns + "authorization-amount")
.Value;
var googleNumber =
xmlData
.Root
.Element(ns + "google-order-number")
.Value;
_checkoutService.ChargeAndShip(googleNumber, amount);
charged = true;
}
What about...
if(xmlData.Root.Name.LocalName.Equals("new-order-notification")){
....
}
But the xml you posted doesn't seem to match the code your using.. The elements do not exist

Categories