Search code examples
htmlxmlrazorxpathasp.net-webpages

ASP.NET Razor Pages: Create HTML Representing Structure of Xml


I am trying to display the data in this xml(~/server_settings.xml):

<?xml version="1.0" encoding="utf-8"?>
<site>
    <general>
        <name>Venture Corp</name>
        <url>http://anerdsplayground.com/</url>
        <developer>Otard95</developer>
    </general>
    <ore_contracts>
        <use_system description="This is the system id the ore contract app gets its prices from">30000142</use_system>
    </ore_contracts>
</site>

on my site. But I want to give it some style with css and add a form with input for each node that has a text value, so that I can later edit the content.

I want to be able to read any new nodes as well, as this file is going to expand in the future. So essentially it needs to read a dynamic xml.

I'm currently using this code:

@using System.Xml
@using System.Xml.Linq
@using System.Xml.XPath

@functions{

    XmlDocument doc = null;
    string output = "";

    XmlDocument set_doc{
        set{
            doc = value;
        }
    }

    string write_xml(string path){

        // Add start tag and any attributes that this node may have
        output += "<div ";

        if(doc.SelectSingleNode(path).Attributes.Count > 0){
            // As the description attribute is to be added to a p tag save it for later
            string desc = "";

            // loop through all attributes
            foreach(XmlAttribute att in doc.SelectSingleNode(path).Attributes){
                if(att.Name == "description"){ // If description attribute exists save it for later
                    desc = att.InnerText;
                }else{ // Write attribute to output
                    output += att.Name + '=' + '"' + att.InnerText + '"' + ' ';
                }
            }
            // Close starting tag
            output += ">";

            // If node has description attribute add to <p> tag
            if(desc != ""){
                output += "<p>" + desc + "</p><br/>";
            }
        }

        // If current node has one or more child nodes execute this function for each of them
        if(doc.SelectSingleNode(path).HasChildNodes){
            foreach(XmlNode next_node in doc.SelectSingleNode(path).ChildNodes){
                string new_path = path + "/" + next_node.Name; // Create path for next element
                write_xml(new_path); // write next inner node
            }

            // Close this node
            output += "</div>";
        }else{ // No inner nodes add content of current node and close element
            output += "<input type=" + '"' + "text" + '"' + " name=" + '"' + "node_content" + '"' + " value=" + '"' + doc.SelectSingleNode(path).InnerText + '"' + " />";
        }
        return output;
    }
}

@{
    // Get xml from eve-centeral with current ore-prices and creat dictionary
    XmlDocument doc = new XmlDocument();
    doc.Load(Server.MapPath("~/server_settings.xml"));
    string root = "/site";

    set_doc = doc;
}

<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title></title>
        <style>

        </style>
    </head>
    <body>
        @Html.Raw(write_xml(root));
    </body>
</html>

But I get this error:

Server Error in '/' Application.

Expression must evaluate to a node-set.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Xml.XPath.XPathException: Expression must evaluate to a node-set.

Source Error: 


Line 19:         output += "<div ";
Line 20: 
Line 21: error-->if(doc.SelectSingleNode(path).Attributes.Count > 0){
Line 22:             // As the description attribute is to be added to a p tag save it for later
Line 23:             string desc = "";

Line: 21 

Stack Trace: 


[XPathException: Expression must evaluate to a node-set.]
   MS.Internal.Xml.XPath.XPathParser.ParseNodeTest(AstNode qyInput, AxisType axisType, XPathNodeType nodeType) +5596737
   MS.Internal.Xml.XPath.XPathParser.ParseStep(AstNode qyInput) +75
   MS.Internal.Xml.XPath.XPathParser.ParseRelativeLocationPath(AstNode qyInput) +18
   MS.Internal.Xml.XPath.XPathParser.ParseLocationPath(AstNode qyInput) +118
   MS.Internal.Xml.XPath.XPathParser.ParsePathExpr(AstNode qyInput) +30
   MS.Internal.Xml.XPath.XPathParser.ParseUnionExpr(AstNode qyInput) +19
   MS.Internal.Xml.XPath.XPathParser.ParseUnaryExpr(AstNode qyInput) +44
   MS.Internal.Xml.XPath.XPathParser.ParseMultiplicativeExpr(AstNode qyInput) +21
   MS.Internal.Xml.XPath.XPathParser.ParseAdditiveExpr(AstNode qyInput) +21
   MS.Internal.Xml.XPath.XPathParser.ParseRelationalExpr(AstNode qyInput) +21
   MS.Internal.Xml.XPath.XPathParser.ParseEqualityExpr(AstNode qyInput) +21
   MS.Internal.Xml.XPath.XPathParser.ParseAndExpr(AstNode qyInput) +19
   MS.Internal.Xml.XPath.XPathParser.ParseOrExpr(AstNode qyInput) +19
   MS.Internal.Xml.XPath.XPathParser.ParseExpresion(AstNode qyInput) +30
   MS.Internal.Xml.XPath.XPathParser.ParseXPathExpresion(String xpathExpresion) +54
   System.Xml.XPath.XPathExpression.Compile(String xpath, IXmlNamespaceResolver nsResolver) +51
   System.Xml.XPath.XPathNavigator.Select(String xpath) +14
   System.Xml.XmlNode.SelectNodes(String xpath) +45
   System.Xml.XmlNode.SelectSingleNode(String xpath) +7
   ASP._Page_Administrative_Edit_Settings_cshtml.write_xml(String path) in c:\Users\Otard95\Google Drive\Personal\WebSites\VentureCorpDrive\Administrative\Edit_Settings.cshtml:21
   ASP._Page_Administrative_Edit_Settings_cshtml.write_xml(String path) in c:\Users\Otard95\Google Drive\Personal\WebSites\VentureCorpDrive\Administrative\Edit_Settings.cshtml:46
   ASP._Page_Administrative_Edit_Settings_cshtml.write_xml(String path) in c:\Users\Otard95\Google Drive\Personal\WebSites\VentureCorpDrive\Administrative\Edit_Settings.cshtml:46
   ASP._Page_Administrative_Edit_Settings_cshtml.write_xml(String path) in c:\Users\Otard95\Google Drive\Personal\WebSites\VentureCorpDrive\Administrative\Edit_Settings.cshtml:46
   ASP._Page_Administrative_Edit_Settings_cshtml.Execute() in c:\Users\Otard95\Google Drive\Personal\WebSites\VentureCorpDrive\Administrative\Edit_Settings.cshtml:78
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy() +197
   System.Web.WebPages.WebPage.ExecutePageHierarchy(IEnumerable`1 executors) +68
   System.Web.WebPages.WebPage.ExecutePageHierarchy() +151
   System.Web.WebPages.WebPageBase.ExecutePageHierarchy(WebPageContext pageContext, TextWriter writer, WebPageRenderingBase startPage) +76
   System.Web.WebPages.WebPageHttpHandler.ProcessRequestInternal(HttpContextBase httpContext) +114

Desired output (example):

<html lang="en">
    <body>
        <div>NodeName:
            <div any attributes xml node might have>
                <p>value of attribute named "description"</p>
                <form method="post">
                    <input type="text" name="node_content" value="current content of xml node"/>
                </form>
            </div>
        </div>

        <div any attributes xml node might have>
            <p>value of attribute named "description"</p>
            <form method="post">
                <input type="text" name="node_content" value="current content of xml node"/>
            </form>
        </div>

        ...

    </body>
</html>

I'm quite new to the System.xml namespace so this might not be the best way to do it. But as I have not found a good tutorial, my only choice has been to use the method of trial and error. So if you do decide to reply please explain a little. And if you have a good tutorial handy, a link maybe, I'd greatly appreciate it.

If I missed anything and you need more info, please give me a heads-up and I'll Edit the post as soon as I can.

Thanks in advance for any help you might provide!


Solution

  • As @har07 suggested i found the value of the path variable when the exception occurs, and I got /site/general/name/#text. What i did not know was that the text content of a XmlElement was considered a ChildNode

    Solution:

    if(doc.SelectSingleNode(path).HasChildNodes && doc.SelectSingleNode(path).FirstChild.GetType().ToString() != "System.Xml.XmlText"){
        foreach(XmlNode next_node in doc.SelectSingleNode(path).ChildNodes){
            string new_path = path + "/" + next_node.Name; // Create path for next element
            write_xml(new_path); // write next inner node
        }
    
        // Close this node
        output += "</div>";
    }else{
    

    Edit:

    Added && doc.SelectSingleNode(path).FirstChild.GetType().ToString() != "System.Xml.XmlText" to if statement

    Many thanks to you @har07