Can someone help me with a line of code to access an iCal node from an RSS feed?
Specifically i want to access the xCal:x-calconnect-venue-name node.
My parent node is 'item', so the path is:
item/xCal:x-calconnect-venue/xCal:adr/xCal:x-calconnect-venue-name
How can i use parent.SelectChildNode() to access the value of that node?
Many thanks
b
If the RSS item contents is something like this (irrelevant nodes omitted)
<item>
<xCal:adr>
<xCal:x-calconnect-venue-name>venue name</xCal:x-calconnect-venue-name>
</xCal:adr>
</item>
Then you could do
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.xmlDoc);
nsmgr.AddNamespace("xCal", "urn:ietf:params:xml:ns:xcal");
// possibly add the RSS namespace as well?
XmlNodeList nodes = xmlDoc.SelectNodes("rss/channel/item");
foreach (XmlNode node in nodes) {
XmlNode venue = node.SelectSingleNode(".//xCal:x-calconnect-venue-name", nsmgr);
// watch out, there might not be a select result!
if (venue != null) {
string s = venue.InnerText;
// ...
}
}
or directly
string xpath = "rss/channel/item//xCal:x-calconnect-venue-name";
XmlNodeList nodes = xmlDoc.SelectNodes(xpath, nsmgr);
foreach (XmlNode venue in nodes) {
string s = venue.InnerText;
// ...
}
Related
I am writing an ASP.NET Core 6 MVC app, and I have to read an XML with this format:
<?xml version="1.0" encoding="utf-8" ?>
<taglines common-copyright="©2007 ">
<tagline brandID="1" brandName="BCBS" >
<registered registered="®Registered">
</registered>
<copyright copyright="©2007">
</copyright>
<tagline2 tagline2="Data.">
</tagline2>
</tagline>
</taglines>
I need to read the content in tag tagline2
The way I am doing it is
private string ReadXMLAnthemFile()
{
string footer = "";
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(String.Concat(this.Environment.WebRootPath,"/img/TagLines.xml"));
foreach (XmlNode node in xmlDocument.SelectNodes("taglines/tagline/tagline2"))
{
footer = node.OuterXml;
}
return footer;
}
But node.OuterXml returns the entire tag
<tagline2 tagline2="Data."></tagline2>
How can I get only the data inside the tag?
Thanks
You don't need to read the "outer" XML - you need to reach into the XML node you've fetched, and read it's tagline2 attribute - if present.
Try something like this:
foreach (XmlNode node in xmlDocument.SelectNodes("taglines/tagline/tagline2"))
{
if (node.Attributes["#tagline2"] != null)
{
footer = node.Attributes["#tagline2"].Value;
}
}
That should get you the .Value of the tagline2 attribute of the XML node that your XPath expression matches - the value would be Data. in your case here.
I am trying to find a way to pass in an optional string list to a query. What I am trying to do is filter a list of tags by the relationship between them. For example if c# was selected my program would suggest only tags that appear in documents with a c# tag and then on the selection of the next, say SQL, the tags that are linked to docs for those two tags together would be shown, whittling it down so that the user can get closer and closer to his goal.
At the moment all I have is:
List<Tag> _tags = (from t in Tags
where t.allocateTagDoc.Count > 0
select t).ToList();
This is in a method that would be called repeatedly with the optional args as tags were selected.
I think I have been coming at it arse-backwards. If I make two(or more) queries one for each supplied tag, find the docs where they all appear together and then bring out all the tags that go with them... Or would that be too many hits on the db? Can I do it entirely through an entity context variable and just query the model?
Thanks again for any help!
You can try this.
First collect tag to search in a list of strings .
List<string> tagStrings = new List<string>{"c#", "sql"};
pass this list in your query, check whether it is empty or not, if empty, it will return all the tags, else tags which matches the tagStrings.
var _tags = (from t in Tags
where t.allocateTagDoc.Count > 0
&& (tagStrings.Count ==0 || tagStrings.Contains(t.tagName))
select t).ToList();
You can also try this, Dictionary represents ID of a document with it's tags:
Dictionary<int, string[]> documents =
new Dictionary<int, string[]>();
documents.Add(1, new string[] { "C#", "SQL", "EF" });
documents.Add(2, new string[] { "C#", "Interop" });
documents.Add(3, new string[] { "Javascript", "ASP.NET" });
documents.Add(4, new string[] { });
// returns tags belonging to documents with IDs 1, 2
string[] filterTags = new string[] { "C#" };
var relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
// returns tags belonging to document with ID 1
filterTags = new string[] { "C#", "SQL" };
relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
// returns tags belonging to all documents
// since no filtering tags are specified
filterTags = new string[] { };
relatedTags = GetRelatedTags(documents, filterTags);
Debug.WriteLine(string.Join(",", relatedTags));
public static string[] GetRelatedTags(
Dictionary<int, string[]> documents,
string[] filterTags)
{
var documentsWithFilterTags = documents.Where(o =>
filterTags
.Intersect(o.Value).Count() == filterTags.Length);
string[] relatedTags = new string[0];
foreach (string[] tags in documentsWithFilterTags.Select(o => o.Value))
relatedTags = relatedTags
.Concat(tags)
.Distinct()
.ToArray();
return relatedTags;
}
Thought I would pop back and share my solution which was completely different to what I first had in mind.
First I altered the database a little getting rid of a useless field in the allocateDocumentTag table which enabled me to use the entity framework model much more efficiently by allowing me to leave that table out and access it purely through the relationship between Tag and Document.
When I fill my form the first time I just display all the tags that have a relationship with a document. Using my search filter after that, when a Tag is selected in a checkedListBox the Document id's that are associated with that Tag(s) are returned and are then fed back to fill the used tag listbox.
public static List<Tag> fillUsed(List<int> docIds = null)
{
List<Tag> used = new List<Tag>();
if (docIds == null || docIds.Count() < 1)
{
used = (from t in frmFocus._context.Tags
where t.Documents.Count >= 1
select t).ToList();
}
else
{
used = (from t in frmFocus._context.Tags
where t.Documents.Any(d => docIds.Contains(d.id))
select t).ToList();
}
return used;
}
From there the tags feed into the doc search and vice versa. Hope this can help someone else, if the answer is unclear or you need more code then just leave a comment and I'll try and sort it.
I'm trying to get the select element for a particular webpage, but I have trouble doing this.
Here's my code so far.
I'm trying to get the select element in a web page, containing the name "postalDistrictList", and none of my code works.
I also tried htmlweb.DocumentNode.SelectNodes("//select") but this returns null.
Does anyone have any idea how I can do this?
static void Main(string[] args)
{
HtmlNode.ElementsFlags.Remove("option");
HtmlWeb htmlweb = new HtmlWeb();
HtmlDocument html = htmlweb.Load("https://www.ura.gov.sg/realEstateWeb/realEstate/pageflow/transaction/submitSearch.do");
// HtmlNode bodyNode = html.DocumentNode.SelectNodes("//select");
HtmlNode bodyNode = html.DocumentNode.SelectSingleNode("/html/body");
HtmlNode selectNode = html.GetElementbyId("postalDistrictList");
HtmlNodeCollection selectNodes = html.DocumentNode.SelectNodes("//select[#name='postalDistrictList']");
// HtmlNode selectNode = html.DocumentNode.SelectSingleNode("//select[#name='postalDistrictList']");
HtmlNode node = selectNode;
// foreach (HtmlNode node in selectNodes)
{
Console.Out.Write(node.Attributes["options"].Value);
Console.Out.WriteLine();
}
}
Try the XPath //./select[#name='postalDistrictList'], i.e.
HtmlNodeCollection selectNodes = html.DocumentNode.SelectNodes("//./select[#name='postalDistrictList']");
should help you get the a collection of the select elements you are looking for.
I want to sort a XML-Document by attributes of an Child-Node. I am working with Windows Forms in Visual C++. I tried to use the solution proposed here on Stackoverflow. But somehow it won't work.
My unsorted XML-Doc looks like the following:
<Message-List>
<Message sendTime="0"></Message>
<Message sendTime="20"></Message>
<Message sendTime="5"></Message>
</Message-List>
My sorted XML-Doc should look like this:
<Message-List>
<Message sendTime="0"></Message>
<Message sendTime="5"></Message>
<Message sendTime="20"></Message>
</Message-List>
I tried following Code, but the checkme-String returned is 0205. So the sorting doesn't even work.
System::Xml::XmlDocument^ sourceXmlDoc = gcnew XmlDocument;
sourceXmlDoc->LoadXml("<Message-List><Message sendTime=\"0\"></Message><Message sendTime=\"20\"></Message><Message sendTime=\"5\"></Message></Message-List>");
System::Xml::XPath::XPathNavigator^ navigator = sourceXmlDoc->CreateNavigator();
System::Xml::XPath::XPathExpression^ selectExpression = navigator->Compile("Message-List/Message");
System::Xml::XPath::XPathExpression^ sortExpr = navigator->Compile("#sendTime");
selectExpression->AddSort(sortExpr, XmlSortOrder::Ascending, XmlCaseOrder::None, "", XmlDataType::Text);
System::Xml::XPath::XPathNodeIterator^ nodeIterator = navigator->Select(selectExpression);
String^ checkMe;
while (nodeIterator->MoveNext())
{
if (nodeIterator->Current->MoveToFirstAttribute())
{
checkMe = checkMe + nodeIterator->Current->Value;
}
}
Furthermore, I am stuck on how to proceed in the while-loop. How can I save the resorted xmlDoc as XmlDocument?
I might be completely wrong since .NET is not my thing, but isn't your xpath in navigator->Compile wrong and your XML data type in selectExpression->AddSort wrong
System::Xml::XPath::XPathExpression^ selectExpression = navigator->Compile("Message-List/Message");
selectExpression->AddSort(sortExpr, XmlSortOrder::Ascending, XmlCaseOrder::None, "", XmlDataType::Number);
Finally, I managed to sort the XML-Message.
#John: yes you were right, I missed to change the XmlDataType. Thanks for the hint.
The following Solution makes a Copy of a XmlDocument and sorts it by the numerical Attribute "sendTime".
// Create Source-Document for Testing
System::Xml::XmlDocument^ sourceXmlDoc = gcnew XmlDocument;
sourceXmlDoc->LoadXml("<Message-List><Message sendTime=\"0\"></Message><Message sendTime=\"20\"></Message><Message sendTime=\"5\"></Message></Message-List>");
// Create a copy of the input XmlDocument
System::Xml::XmlDocument^ xmlDocCopy = gcnew XmlDocument;
xmlDocCopy = safe_cast<XmlDocument^>(sourceXmlDoc->Clone());
// Only needs to be resorted if there are Messages to be sorted
if ( xmlDocCopy->HasChildNodes )
{
// Remove the unsorted Children
xmlDocCopy->FirstChild->RemoveAll();
// Create a sorted Navigator
System::Xml::XPath::XPathNavigator^ navigator = sourceXmlDoc->CreateNavigator();
System::Xml::XPath::XPathExpression^ selectExpression = navigator->Compile("Message-List/Message");
System::Xml::XPath::XPathExpression^ sortExpr = navigator->Compile("#sendTime");
selectExpression->AddSort(sortExpr, XmlSortOrder::Ascending, XmlCaseOrder::None, "", XmlDataType::Number);
System::Xml::XPath::XPathNodeIterator^ nodeIterator = navigator->Select(selectExpression);
String^ checkMe;
String^ test = nodeIterator->Current->OuterXml;
while (nodeIterator->MoveNext())
{
XmlTextReader^ xmlChildReader = gcnew XmlTextReader(gcnew StringReader(nodeIterator->Current->OuterXml));
XmlNode^ newNode = xmlDocCopy->ReadNode(xmlChildReader);
xmlDocCopy->FirstChild->AppendChild(newNode);
}
}
I just saw this solution, but in .NET 4.0 you could use linq instead. I won't port it to C++, but you'll get the idea! It is a static function that sorts by attribute and the call to this method that replaces the old node with the sorted node.
public static XmlElement OrderChildrenByAttribute(XmlElement originalElement, string attributeName)
{
// Sorting products
var enumList = originalElement.ChildNodes.Cast<XmlElement>();
var sortedList = enumList.OrderBy(p => p.GetAttribute(attributeName).Trim().ToLower());
XmlElement result = originalElement.OwnerDocument.CreateElement(originalElement.Name, originalElement.NamespaceURI);
foreach (var childElem in sortedList)
result.AppendChild(childElem);
return result;
}
And for the call, I use this:
// Sort by "name" attribute
XmlElement elem_params_Products_sorted = XmlTools.OrderChildrenByAttribute(elem_params_Products, "name");
elem_params_Products.ParentNode.ReplaceChild(elem_params_Products_sorted, elem_params_Products);
I have an XElement which I am getting after parsing an xml. This XElement needs to be read only when the need arises so, I have stored it in a list for future use.
I have to read this XElement using Linq.
XDocument doc = XDocument.Parse(DataManager.offeringElements[index].DataElem.ToString());
var docNode = from dataNode in doc.Descendants("DataLinks")
select new
{
Offering = dataNode .Element("link").Value,
linkUrl = dataNode.Element("link").Attribue("href").Value
};
the Xelement has the following nodes
<DataLinks>
<link href="">a. Management</link>
<link href="">b. Analytics</link>
<link href="">c. Development</link>
</DataLinks>
My problem is that I am not able to read all the three nodes.I am able to get only the first node. Where is it going wrong?
This should work i.e. Elemenets not Descendants and ensure when selecting new, you are selecting a type of object.
public class ObjectType
{
public Offering {get; set;}
public linkUrl {get; set;}
}
var docNode = from dataNode in doc.Elemenets ("DataLinks")
select new ObjectType
{
Offering = dataNode .Element("link").Value,
linkUrl = dataNode.Element("link").Attribue("href").Value
};
I would do it like this. IMHO, the method syntax looks cleaner then the query syntax. Note that this is untested code.
IEnumerable<XElement> seqLinks = doc.Descendants("DataLinks").Single()
.Descendants("link");
foreach(XElement link in seqLinks)
{
Console.WriteLine("Value is {0}, and href is {1}",
link.Value, link.Attribute("href").Value)
}