【问题标题】:Parse controls in an aspx file and convert it to xml解析 aspx 文件中的控件并将其转换为 xml
【发布时间】:2010-06-04 07:14:29
【问题描述】:

我需要解析 aspx 文件(从磁盘,而不是在浏览器上呈现的文件)并列出页面上存在的所有服务器端 asp.net 控件的列表,然后从中创建一个 xml 文件.最好的方法是什么?另外,有没有可用的库?

例如,如果我的 aspx 文件包含

<asp:label ID="lbl1" runat="server" Text="Hi"></asp:label>

我的 xml 文件将是

<controls>
<ID>lbl1</ID>
<runat>server</runat>
<Text>Hi</Text>
</controls>

【问题讨论】:

  • 上一个问题中的“this”是什么意思(“另外,有没有可用的库呢?”)?解析 XML(-> 是的,有(一堆)库)或在(有效的)ASPX 文件中查找控件(-> 不太确定是否有相应的库)。

标签: c# asp.net xml


【解决方案1】:

Xml 解析器无法理解 ASP 指令:

您可能最好使用正则表达式来执行此操作,可能分 3 个阶段。

  1. 匹配整个页面中的任何标签元素。
  2. 对于每个标签,匹配标签和控件类型。
  3. 对于匹配 (2) 的每个标签,匹配任何属性。

所以,从顶部开始,我们可以使用以下正则表达式:

(?<tag><[^%/](?:.*?)>)

这将匹配任何没有

<asp:Content ID="ph_PageContent" ContentPlaceHolderID="ph_MainContent" runat="server">
<asp:Image runat="server" />
<img src="/test.png" />

对于每个捕获的标签,我们要提取标签并输入:

<(?<tag>[a-z][a-z1-9]*):(?<type>[a-z][a-z1-9]*)

创建命名的捕获组使这更容易,这将使我们能够轻松地提取标签和类型。这只会匹配服务器标签,因此此时标准的 html 标签将被删除。

<asp:Content ID="ph_PageContent" ContentPlaceHolderID="ph_MainContent" runat="server">

将产生:

{ tag = "asp", type = "Content" }

使用相同的标签,我们可以匹配任何属性:

(?<name>\S+)=["']?(?<value>(?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?

产量:

{ name = "ID", value = "ph_PageContent" },
{ name = "ContentPlaceHolderID", value = "ph_MainContent" },
{ name = "runat", value = "server" }

因此,将所有这些放在一起,我们可以创建一个可以为我们创建 XmlDocument 的快速函数:

public XmlDocument CreateDocumentFromMarkup(string content)
{
  if (string.IsNullOrEmpty(content))
    throw new ArgumentException("'content' must have a value.", "content");

  RegexOptions options = RegexOptions.CultureInvariant | RegexOptions.Compiled | RegexOptions.IgnoreCase;
  Regex tagExpr = new Regex("(?<tag><[^%/](?:.*?)>)", options);
  Regex serverTagExpr = new Regex("<(?<tag>[a-z][a-z1-9]*):(?<type>[a-z][a-z1-9]*)", options);
  Regex attributeExpr = new Regex("(?<name>\\S+)=[\"']?(?<value>(?:.(?![\"']?\\s+(?:\\S+)=|[>\"']))+.)[\"']?", options);

  XmlDocument document = new XmlDocument();
  XmlElement root = document.CreateElement("controls");

  Func<XmlDocument, string, string, XmlElement> creator = (document, name, value) => {
    XmlElement element = document.CreateElement(name);
    element.InnerText = value;

    return element;
  };

  foreach (Match tagMatch in tagExpr.Matches(content)) {
    Match serverTagMatch = serverTagExpr.Match(tagMatch.Value);

    if (serverTagMatch.Success) {
      XmlElement controlElement = document.CreateElement("control");

      controlElement.AppendChild(
        creator(document, "tag", serverTagMatch.Groups["tag"].Value));
      controlElement.AppendChild(
        creator(document, "type", serverTagMatch.Groups["type"].Value));


      XmlElement attributeElement = document.CreateElement("attributes");

      foreach (Match attributeMatch in attributeExpr.Matches(tagMatch.Value)) {
        if (attributeMatch.Success) {
          attributeElement.AppendChild(
            creator(document, attributeMatch.Groups["name"].Value, attributeMatch.Groups["value"].Value));
        }
      }

      controlElement.AppendChild(attributeElement);
      root.AppendChild(controlElement);
    }
  }  

  return document;
}

生成的文档可能如下所示:

<controls>
  <control>
    <tag>asp</tag>
    <type>Content</type>
    <attributes>
      <ID>ph_PageContent</ID>
      <ContentPlaceHolderID>ph_MainContent</ContentPlaceHolderID>
      <runat>server</runat>
    </attributes>
  </control>
</controls>

希望有帮助!

【讨论】:

  • 嵌入式用户控件呢?您的程序不会记录来自那里的控件。 +1 解决方案
  • @Yauheni,对于用户控件,您不会解析 .ascx 文件吗?
  • 由于我的编辑被拒绝,我将其作为评论发布。这是第三个正则表达式 (?&lt;name&gt;\\S+)=[\"']&lt;?(?&lt;value&gt;(?:.?(?![\"']?\\s+(?:\\S+)=|[&gt;\"']))+.)[\"']? 的改进版本,它甚至可以从普通的 HTML 元素中提取信息,而不仅仅是 ASP 控件。此外,修复了当正则表达式无法找到属性值只是一个字符的值时的问题。例如&lt;a href="#"&gt; 只是回馈社区的一个小方法,它帮助了我。
  • 非常好的代码,但它与多行编写的控件不匹配。在这种情况下应该添加 RegexOptions.Singleline
  • 在我的特定场景中,我的一个属性中实际上有一个 base64 编码值,并且该值以 = 结尾(即属性 =“...W91dD4 =”)。最后一个正则表达式存在问题,它将值包含在名称组中。为了解决我的问题,我在名称组的末尾添加了一个问号来进行非贪婪匹配。名称组部分最终如下: (?\\S+?)
【解决方案2】:

我在上面的代码中使用了以下三个正则表达式,它也给了我 html 标签。我也可以获取开始标签和结束标签之间的值。

Regex tagExpr = new Regex("(?<tag><[^%/](?:.*?)>[^/<]*)", options);
Regex serverTagExpr = new Regex("<(?<type>[a-z][a-z1-9:]*)[^>/]*(?:/>|[>/])(?<value>[^</]*)", options);
Regex attributeExpr = new Regex("(?<name>\\S+)=[\"']?(?<value>(?:.(?![\"']?\\s+(?:\\S+)=|[>\"']))+.)[\"']?", options);

【讨论】:

    【解决方案3】:
     Func<XmlDocument, string, string, XmlElement> creator = (document, name, value) => {
    XmlElement element = document.CreateElement(name);
    element.InnerText = value;
    

    上述通用模板将适用于version 3.5 及更高版本。所以如果有人使用低于该版本的版本,请创建如下函数:

    public XmlElement creator(XmlDocument document, string name, string value)
    {
        XmlElement element = document.CreateElement(name);
        element.InnerText = value;
    
        return element;
    }
    

    这会起作用

    【讨论】:

      【解决方案4】:

      ASPX 文件应该是有效的 XML,所以也许 XSLT 会是一个很好的解决方案。 W3 Schools site 有很好的介绍和参考。然后,您可以从一个简单的程序调用此 XSLT 来选择所需的文件。

      或者,您可以使用 Linq to XML 加载 ASPX 文件并以 Linq 样式迭代控件。

      【讨论】:

      • 感谢您的回复,但是您是否有任何网站可以开始使用示例代码/示例项目?我是 .net 中的新手 :)
      • 当我 XDocument.Parse 我得到 Name cannot be with '%' 字符。页面以&lt;%@ Control Language="C#" AutoEventWireup="true" CodeFile="DealSummary.ascx.cs" Inherits="Pages_UserControls_DealSummary" %&gt; 开头
      • 没有要求 aspx 页面是有效的 XML。单独的 Page 或控制指令将导致此方法失败
      【解决方案5】:

      如果标签的代码多于一行,我们在提取标签数据时可能会遇到问题。为了避免我从我们传递给上述函数(内容)的源字符串中删除了如下换行符

      string contentRemovedNewLines = Regex.Replace(content, @"\t|\n|\r", "");
      

      那么我们可以使用 contentRemovedNewLines 来代替 content。

      上面的代码可以按我的意愿工作。还可以添加一件事。你可以调用上面的方法,如下图,然后保存为xml文件,我们可以检查是否有预期的结果。

      XmlDocument xmlDocWithWebContent = CreateDocumentFromMarkup(sourceToRead);
      
      string xmlfileLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "tempXmlOutputFileOfWebSource.xml";
      
      xmlDocWithWebContent.Save(xmlfileLocation);
      

      为此,我们必须有一个 xml 文件的根元素

                  XmlDocument document = new XmlDocument();
                  XmlNode xmlNode = document.CreateNode(XmlNodeType.XmlDeclaration, "", "");
                  XmlElement root = document.CreateElement("controls");
                  document.AppendChild(root);
      

      我使用了上面的修复方法

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-10-18
        • 1970-01-01
        • 1970-01-01
        • 2021-01-09
        • 1970-01-01
        • 2013-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多