I have this string of XML elements in a document
<dmc><avee><modelic></modelic><sdc></sdc><chapnum></chapnum><section></section>
<subsect></subsect><subject></subject><discode></discode><discodev></discodev>
<incode></incode><incodev></incodev><itemloc></itemloc></avee></dmc>
What I need to do is now populate those elements with user inputted variables using Linq. I currently have:
XDocument doc = XDocument.Load(sgmlReader);
doc.Element("modelic").Add(MI);
doc.Element("sdc").Add(sd);
doc.Element("chapnum").Add(sys);
doc.Element("section").Add(subsys);
doc.Element("subsect").Add(subsubsys);
doc.Element("subject").Add(unit);
doc.Element("discode").Add(dc);
doc.Element("discodev").Add(dcv);
doc.Element("incode").Add(infcode);
doc.Element("incodev").Add(infCV);
doc.Element("itemloc").Add(loc);
(yes I'm using sgmlReader but this works fine in my program in other areas) I'm clearly missing something fundamental as it's giving me a NullReferenceException was unhandled - Object reference not set to an instance of an object.
Any ideas/suggestions please?
The Element() method only matches the immediate children of the container.
You can either chain Descendants() into First():
doc.Descendants("modelic").First().Add(MI);
Or navigate to the immediate parents of the elements you want to modify:
doc.Root.Element("avee").Element("modelic").Add(MI);
This should work:
var avee = dmc.Root.Element("avee");
avee.Element("modelic").Value = MI;
avee.Element("sdc").Value = sd;
Just repeat the last line for each of your remaining elements (chapnum, section...).
The problem was that first you have to retrieve root element (dmc), then avee, and then you can set values for child elements of avee.
Related
Is there a way to create DOM elements using string in AngleSharp? For example:
var result = document.Create...("<div id='div1'>hi<p>world</p></div>");
This is possible using a document fragment.
There are multiple possibilities how to use a document fragment, one way would be to use fragment parsing for generating a node list in the right context:
var context = BrowsingContext.New(Configuration.Default);
var document = await context.OpenAsync(r => r.Content("<div id=app><div>Some already available content...</div></div>"));
var app = document.QuerySelector("#app");
var parser = context.GetService<IHtmlParser>();
var nodes = parser.ParseFragment("<div id='div1'>hi<p>world</p></div>", app);
app.Append(nodes.ToArray());
The example shows how nodes can be created in the context of a certain element (#app in this case) and that the behavior is different than, e.g., using InnerHtml, which would remove existing nodes.
Hope this helps!
As we all know Roslyn Syntax Trees are Immutable, so after making changes you need to get a new node.
I'm trying to update a document using the document editor, but I keep getting an error that the node is not found in the syntax tree.
public static T FindEquivalentNode<T>(this Document newDocument, T node)
where T : CSharpSyntaxNode
{
var root = newDocument.GetSyntaxRootAsync().Result;
return root.DescendantNodes().OfType<T>()
.FirstOrDefault(newNode => SyntaxFactory.AreEquivalent(newNode, node));
}
When I try to Call this again the document editor:
var newFieldDeclaration = documentEditor.GetChangedDocument().FindEquivalentNode(syntaxNode);
documentEditor.ReplaceNode(newFieldDeclaration, propertyDeclaration);
I get an error:
The node is not part of the tree
The newField Declaration is not null it find an equivalent field yet I still get this error, How Can I Replace this node?
You get the node because in your FindEquivalentNode method, you are doing that:
SyntaxFactory.AreEquivalent(newNode, node)
SyntaxFactory.AreEquivalent is not return true for "real" same nodes, but for nodes\tokens that are seems equal in their structure (with consideration of topLevel parameter).
Back to your question, if you want to call ReplaceNode you must have the "real" old node, so you wouldn't get an
The node is not part of the tree
To achive that, you have a few options, one of them is what #Tamas wrote in comments, use SyntaxAnnotations.
Example:
//Add annotation to node
var annotation = new SyntaxAnnotation("your_annotation", "some_data");
node = node .WithAdditionalAnnotations(annotation);
// Now you can change the tree as much you want
// And when you want to get the node from the changed tree
var annotatedNode = someNode.DescendantNodesAndSelf().
FirstOrDefault(n => n.GetAnnotations("your_annotation").Any())
I have a large XML document and I am using C# to query the content. I have something similar to the following:
var bookA = xmlDoc.SelectSingleNode("/books/book[yearPublished=2012 and id=123]");
var bookB = xmlDoc.SelectSingleNode("/books/book[yearPublished=2012 and id=456]");
var bookC = xmlDoc.SelectSingleNode("/books/book[yearPublished=2012 and id=789]");
As you can see, the filter on "yearPublished" is repeated in each query and, please correct me if I'm wrong, the entire list of books is parsed repeatedly. Would it be more efficient to have something like the following:
var newBooks = xmlDoc.SelectNodes("/books/book[yearPublished=2012]");
var bookA = newBooks.SelectSingleNode("book[id=123]");
var bookB = newBooks.SelectSingleNode("book[id=456]");
var bookC = newBooks.SelectSingleNode("book[id=789]");
Assuming my document is large (let's say it contains the data about several thousand books), am I correct that it would be more efficient to filter the data based on the first criteria and then select the desired XmlNode from the filtered list?
Secondly, I was trying to validate my assumption but I am having trouble. I get an error message about SelectSingleNode:
'System.Xml.XmlNodeList' does not contain a definition for
'SelectSingleNode' and no extension method 'SelectSingleNode'
accepting a first argument of type 'System.Xml.XmlNodeList' could be
found (are you missing a using directive or an assembly reference?)
I have a reference to System.Xml and also "using System.Xml". Am I missing something else?
The SelectNodes method returns an XmlNodeList type. The method SelectSingleNode belongs to the XmlNode class.
You can get your books like this:
var bookA = newBooks.Where(x => x.Attributes["id"].Value == 123);
var bookB = newBooks.Where(x => x.Attributes["id"].Value == 456);
var bookC = newBooks.Where(x => x.Attributes["id"].Value == 789);
var newBooks = xmlDoc.SelectNodes("/books/book[yearPublished=2012]");
This will return a XmlNodeList object. This object does not contain a method called SelectSingleNode, which is why the compiler is telling you that.
You can iterate over nodes in the XmlNodeList returned from the SelectNodes method like so:
foreach (XmlNode node in newBooks)
{
//...
}
As for your performance issue, or perceived performance issue, I suggest you load a file of your desired size and benchmark it. Only then can you tell if you have a performance problem, as you will have measured it.
You can evaluate an XPath expression that has variable references -- see XPathExpression.SetContext() and this example how to implement an IXsltContextVariable.
I am new to LINQ. I have an ordinary siteMap XML document with custom attributes. One of these attributes is: id
I would like to use LINQ to retrieve a single node matching the value of the custom attribute (id).
etc.
My attempt at the LINQ looks like this:
private SiteMapNode FindNodeById(SiteMapNodeCollection nodes, int siteMapNodeId)
{
var pageNode = from SiteMapNode node in nodes.Cast<SiteMapNode>()
where node["id"] == Convert.ToString(siteMapNodeId)
select node;
return (SiteMapNode)pageNode;
}
During debugging, pageNode becomes assigned with:
{System.Linq.Enumerable.WhereEnumerableIterator<System.Web.SiteMapNode>}
And on the return statement an InvalidCastException is thrown:
Unable to cast object of type 'WhereEnumerableIterator`1[System.Web.SiteMapNode]' to type 'System.Web.SiteMapNode'.
Any help is appreciated! :)
EDIT: I've re-posted this question in a clearer manner here: Re-worded Question
Thanks to Stefan for putting me on the right track!
You try to cast a IEnumerable<SiteMapNode> to a SiteMapNode. Use First to filter and return one node:
return nodes
.Cast<SiteMapNode>()
.First(node => node["id"] == Convert.ToString(siteMapNodeId));
pageNode is a sequence of nodes.
You want to call First() to get the first item in the sequence.
I want to get all visible columns (Hidden == false) for specific list in sharePoint site, I tried to look through the SharePointWebService.Lists.GetList(listName), but couldn't find anything useful, also checked the methods provided by the Lists WebService and also nothing new,
Please advice.
You can use the GetListAndView method of the Lists web service to get the schemas for the list and a view.
From the documentation, if you leave the viewName parameter empty, the default view will be returned. Then, you can read the <ViewFields></ViewFields> node for the list of fields.
*Edit*
Turns out using XPath to query the returned XML was tougher than I thought... here is what I came up with:
XmlNode result = webService.GetListAndView("My Pictures", string.Empty);
XmlNamespaceManager nsmgr = new XmlNamespaceManager(result.OwnerDocument.NameTable);
nsmgr.AddNamespace("sp", "http://schemas.microsoft.com/sharepoint/soap/");
string xpathQuery = "sp:View/sp:ViewFields/sp:FieldRef";
XmlNodeList nodes = result.SelectNodes(xpathQuery, nsmgr);
for (int i = 0; i < nodes.Count; i++)
{
Console.WriteLine(nodes[i].Attributes["Name"].Value);
}
Looks like you have to have a XmlNamespaceManager otherwise your query always returns no values. Something about specifying the namespace... Here is a good reference.
The GetList() method returns a CAML fragment that includes the list's field (column) definitions. You might want to try an XPath expression:
XmlNode list = yourListService.GetList("yourListName");
XmlNodeList visibleColumns
= list.SelectNodes("Fields/Field[not(#Hidden) or #Hidden='FALSE']");
I used the above code but, after a long search I found the solution to get all or custom columns from the sharepoint list. The code for that is shared on ..
Custom Columns or ALL Columns
Solution is quite simple. Using GetList() or similar functions is the wrong way to go.
Instead use GetListContentTypesAsync() to get Content ID and then get the specific ContentType using GetListContentTypeAsync(), it returns XML which includes all visible columns in sharepoint list:
var Contents = await soapListClient.GetListContentTypesAsync(list.Title, "0x01"); // 0x01 is kind of a root element for sharepoint.
String ContentID = Contents.Body.GetListContentTypesResult.Descendants().FirstOrDefault().Attribute("ID").Value.ToString();
var ContentType = await soapListClient.GetListContentTypeAsync(list.Title, ContentID);
XElement xmll = XElement.Parse(ContentType.Body.GetListContentTypeResult.ToString());