【问题标题】:Converting specific HTML structure to specific XML structure将特定的 HTML 结构转换为特定的 XML 结构
【发布时间】:2014-12-31 13:00:09
【问题描述】:

我已经为此苦苦挣扎了一段时间。

我想将 html 转换为 xml。结构如下所示。

我正在使用“HtmlAgilityPack”将 html 转换为有效的 xml 结构。所以,在这之后,我的 HTML 看起来像这样:

<div class="menuItem1" video="" preview="">
    Menu 1
    <div class="subMenu1">
        <div class="menuItem2" video="" preview="">
            Menu 2
            <div class="subMenu2">
                <div class="menuItem3" video="" preview="">
                    Menu 3
                    <div class="subMenu3">
                        <div class="" video="" preview="">Menu 4</div>
                    </div>
                    <div class="treeExpand"></div>
                </div>
                <div class="menuItem3" video="" preview="">Menu 3</div>
                <div class="menuItem3" video="" preview="">Menu 3</div>
            </div>
            <div class="treeExpand"></div>
        </div>
    </div>
    <div class="treeExpand"></div>
</div>
<div class="menuItem1" video="" preview="">
    Menu 1
    <div class="subMenu1">
        <div class="menuItem2" video="" preview="">
            Menu 2
            <div class="subMenu2">
                <div class="menuItem3" video="" preview="">
                    Menu 3
                    <div class="subMenu3">
                        <div class="" video="" preview="">Menu 4</div>
                    </div>
                    <div class="treeExpand"></div>
                </div>
                <div class="menuItem3" video="" preview="">Menu 3</div>
                <div class="menuItem3" video="" preview="">Menu 3</div>
            </div>
            <div class="treeExpand"></div>
        </div>
    </div>
    <div class="treeExpand"></div>
</div>

这正是我想要的。现在我可以使用这个 C# 代码把它变成一个 XElement:

XDocument doc = XDocument.Parse(THE_HTML_STRING_AS_SHOWN_ABOVE);
XDocument docw = new XDocument(new XElement("Navigation", doc.Root));
XElement root = docw.Root;

我创建了一个方法,我可以将根传递给:

GenerateXmlFromHtml(root);

该方法的代码:

private string GenerateXmlFromHtml(XElement elem)
{
    StringBuilder sbNavigationXml = new StringBuilder();
    try
    {
        //HTML will always have a video and preview, according to the generation of the html structure.

        string text = string.Empty;
        string videopath = string.Empty;
        string previewpath = string.Empty;
        XText textNode;

        foreach (XElement element in elem.Elements())
        {
            element.Name = "MenuItem"; //Change element name.

            string htmlClass;
            try { htmlClass = element.Attribute("class").Value; }
            catch { htmlClass = ""; }

            if (!string.IsNullOrEmpty(htmlClass))
            {
                if (htmlClass.Contains("subMenu"))
                {
                    element.AddBeforeSelf(element.Elements());
                    element.Remove();
                    GenerateXmlFromHtml(element);
                }
                else if (htmlClass.Contains("menuItem"))
                {
                    textNode = element.Nodes().OfType<XText>().FirstOrDefault();
                    text = textNode.Value;
                    videopath = element.Attribute("video").Value;
                    previewpath = element.Attribute("preview").Value;

                    if (element.HasElements)
                    {
                        sbNavigationXml.AppendLine("<MenuItem Text=\"" + text + "\" VideoPath=\"" + videopath + "\" PreviewPath=\"" + previewpath + "\">");
                        sbNavigationXml.AppendLine(GenerateXmlFromHtml(element));
                        sbNavigationXml.AppendLine("</MenuItem>");
                    }
                    else
                    {
                        sbNavigationXml.AppendLine("<MenuItem Text=\"" + text + "\" VideoPath=\"" + videopath + "\" PreviewPath=\"" + previewpath + "\" />");
                    }
                }
                else if (htmlClass.Contains("treeExpand"))
                {
                    element.AddBeforeSelf(element.Elements());
                    element.Remove();
                    GenerateXmlFromHtml(element);
                }
            }
            else
            {
                element.AddBeforeSelf(element.Elements());
                element.Remove();
                GenerateXmlFromHtml(element);
            }
        }
    }
    catch (Exception)
    {
        throw;
    }
    return sbNavigationXml.ToString();
}

最后,我希望它产生这个 XML 输出:

<Navigation>
  <MenuItem Text="Menu 1" VideoPath="" PreviewPath="">
    <MenuItem Text="Menu 2">
      <MenuItem Text="Menu 3">
        <MenuItem Text="Menu 4" VideoPath="" PreviewPath="" />
      </MenuItem>
      <MenuItem Text="Menu 3" />
      <MenuItem Text="Menu 3" />
    </MenuItem>
  </MenuItem>
  <MenuItem Text="Menu 1" VideoPath="" PreviewPath="">
    <MenuItem Text="Menu 2">
      <MenuItem Text="Menu 3">
        <MenuItem Text="Menu 4" VideoPath="" PreviewPath="" />
      </MenuItem>
      <MenuItem Text="Menu 3" />
      <MenuItem Text="Menu 3" />
    </MenuItem>
  </MenuItem>
</Navigation>

换句话说,子菜单应该消失,以及树展开 div,然后我想生成 XML,但目前,我仍然失败得很惨。请询问是否有不清楚的地方。任何帮助表示赞赏!

================================================ ==================================================== ==

编辑: 固定的递归方式,想看的人看:

private string GenerateXmlFromHtml(XElement elem)
{
    //HTML will always have a video and preview, according to the generation of the html structure.
    StringBuilder sbNavigationXml = new StringBuilder();
    string text = string.Empty;
    string videopath = string.Empty;
    string previewpath = string.Empty;
    XText textNode;

    try
    {
        foreach (XElement element in elem.Elements())
        {
            //element.Name = "MenuItem"; //Change element name.
            string htmlClass;
            try { htmlClass = element.Attribute("class").Value; }
            catch { htmlClass = ""; }

            if (!string.IsNullOrEmpty(htmlClass))
            {
                if (htmlClass.Contains("subMenu"))
                {
                    if (element.HasElements)
                    {
                        sbNavigationXml.AppendLine(GenerateXmlFromHtml(element));
                    }
                }
                else if (htmlClass.Contains("menuItem"))
                {
                    textNode = element.Nodes().OfType<XText>().FirstOrDefault(); //Get node Text attribute value.
                    text = textNode.Value;
                    videopath = element.Attribute("video").Value; //Get node VideoPath attribute value.
                    previewpath = element.Attribute("preview").Value; //Get node PreviewPath attribute value.

                    if (element.HasElements)
                    {
                        sbNavigationXml.AppendLine("<MenuItem Text=\"" + text + "\" VideoPath=\"" + videopath + "\" PreviewPath=\"" + previewpath + "\">");
                        sbNavigationXml.AppendLine(GenerateXmlFromHtml(element));
                        sbNavigationXml.AppendLine("</MenuItem>");
                    }
                    else
                    {
                        sbNavigationXml.AppendLine("<MenuItem Text=\"" + text + "\" VideoPath=\"" + videopath + "\" PreviewPath=\"" + previewpath + "\" />");
                    }
                }
                else if (htmlClass.Contains("treeExpand"))
                {
                    //DO NOTHING
                }
            }
            else
            {
                if (element.HasElements)
                {
                    sbNavigationXml.AppendLine(GenerateXmlFromHtml(element));
                }
            }
        }
    }
    catch (Exception)
    {
        throw;
    }
    return sbNavigationXml.ToString();
}

【问题讨论】:

  • 旁注:通常人们会以其他方式误解 - 使用 Regex 解析 HTML,但仍然使用适当的 API 构造 XML。为什么需要使用字符串连接来构建 XML?
  • @AlexeiLevenkov - 不,我可以做任何我想做的事情......这只是我所走的路,但任何其他产生 XML 输出的东西都可以,即使我必须这样做完全不同的东西。
  • 查看How can I build XML in C# 以获得指导。
  • @AlexeiLevenkov - 这很好,但我认为我的递归方法有问题。因为我没有看到所有的节点。似乎只到了第二个菜单项级别。
  • 建议:您正在更改 HTML 树,同时遍历它。它使代码几乎无法理解。尽量避免这样做,它可能会自行修复。

标签: c# html xml recursion html-agility-pack


【解决方案1】:

尝试在不同的文档上分离输入和输出。

然后导航输入并开始以您想要的格式将其输出到您的 XmlDocument 输出(另一个变量)。

类似...

class Converter
{
    public XmlDocument Convert(XmlDocument inputDocument)
    {
        XmlDocument result = new XmlDocument();
        ConvertNode(inputDocument.DocumentElement, result.DocumentElement, result);
        return result;
    }

    public void ConvertNode(XmlNode inputNode, XmlNode outputNode, XmlDocument outputDoc)
    {
        XmlNode newNode = null;

        // check elemment class
        string htmlClass;
        try { htmlClass = inputNode.Attributes["class"].Value; }
        catch { htmlClass = ""; }

        if(!string.IsNullOrWhiteSpace(htmlClass))
        {
            if (htmlClass.Contains("menuItem"))
            {
                newNode = outputDoc.CreateElement("MenuItem");
                outputNode.AppendChild(newNode);
            }

            /// check other wanted nodes etc..
        }

        if (newNode != null)
        {
            foreach (XmlNode node in inputNode.ChildNodes)
            {
                ConvertNode(node, newNode, outputDoc);
            }
        }
    }
}

【讨论】:

  • 我正在使用解析器。这就是我如何将 html 转换为有效的 xml 结构,然后我使用 XElement 将其作为普通 xml 处理。
  • 我不明白这是如何回答这个问题的......你有没有看过帖子中的代码?
  • @AlexeiLevenkov 我想这种分离仍然适用。我只改变了重要的部分。
  • 你有没有看过这个帖子 - 你的建议正是 OP 正在做的事情......而且这样的建议应该是评论,而不是答案。
  • 好吧,我知道你想去哪里......但我不能接受这个答案。我做了阿列克谢所说的——不应该在导航时编辑元素。这解决了问题。 - 但是谢谢你的帮助。
猜你喜欢
  • 2014-12-29
  • 1970-01-01
  • 2019-04-04
  • 1970-01-01
  • 2022-01-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-08
相关资源
最近更新 更多