有条件地转义特殊的 XML 字符

6

我搜索了很多,但没有找到一个内置的 .Net 方法可以仅转义特殊的 XML 字符:<, >, &, '",如果它不是一个标记。

例如,考虑以下文本:

Test& <b>bold</b> <i>italic</i> <<Tag index="0" />

我希望你能够将其翻译为:

Test&amp; <b>bold</b> <i>italic</i> &lt;<Tag index="0" />

请注意,标签没有被转义。我基本上需要将这个值设置为XmlElementInnerXML,因此,这些标签必须被保留。
我已经研究过实现自己的解析器,并使用StringBuilder尽可能地进行优化,但这可能会变得非常麻烦。
我还知道哪些标签是可接受的,这可能会简化事情(仅限于:br、b、i、u、blink、flash、Tag)。此外,这些标签可以是自闭合标签。
(e.g. <u />)

或者容器标签
(e.g. <u>...</u>)

2
HTML不是XML...就像<b>foo <i>bar</b> really <br></i>这样。如果你想自己处理,那么你会有很多乐趣。作为一种选择,考虑使用HtmlAgilityPack将HTML解析成合理的树,并仔细地将所有节点插入XML中。 - Alexei Levenkov
任何简单的操作都无法正确处理“测试值为<3但>1”的情况。 - Bobson
@Bobson < 3 不是一个有效的起始标签,所以你可以弄清楚这一点。但你的观点仍然成立,<> 被转义以消除解析中的歧义。在某些情况下,任何合理的解析器都会选择一条路径,而你可能希望选择另一条路径。 - Mike Park
@Bobson 我刚刚编辑了帖子。我已经知道哪些类型的标签是可接受的。此外,<3但>1将需要转义,因为元素不能以空格开头。 - Amir
1
@Amir 这不是最好的例子,但观点是正确的。我本可以说 <3 but >1。然而,只允许使用已知的标签列表会使它变得容易得多 - Bobson
你到底想用转义字符做什么?如果你想将它们添加到XML中,那么只需使用LINQ to XML或其他XML API来编写文本。它们知道如何转义。 - John Saunders
3个回答

3

注意:这个可能可以进行优化。这只是我为你快速编写的东西。请注意,我没有对标签本身进行任何验证。它只是在寻找用尖括号包裹的内容。如果尖括号在标签内被发现(例如<sometag label="我在这里放了一个>"),它也会失败。除此之外,我认为它应该能够完成你要求的工作。

namespace ConsoleApplication1
{
    using System;
    using System.Text.RegularExpressions;

    class Program
    {
        static void Main(string[] args)
        {
            // This is the test string.
            const string testString = "Test& <b>bold</b> <i>italic</i> <<Tag index=\"0\" />";

            // Do a regular expression search and replace. We're looking for a complete tag (which will be ignored) or
            // a character that needs escaping.
            string result = Regex.Replace(testString, @"(?'Tag'\<{1}[^\>\<]*[\>]{1})|(?'Ampy'\&[A-Za-z0-9]+;)|(?'Special'[\<\>\""\'\&])", (match) =>
                {
                    // If a special (escapable) character was found, replace it.
                    if (match.Groups["Special"].Success)
                    {
                        switch (match.Groups["Special"].Value)
                        {
                            case "<":
                                return "&lt;";
                            case ">":
                                return "&gt;";
                            case "\"":
                                return "&quot;";
                            case "\'":
                                return "&apos;";
                            case "&":
                                return "&amp;";
                            default:
                                return match.Groups["Special"].Value;
                        }
                    }

                    // Otherwise, just return what was found.
                    return match.Value;
                });

            // Show the result.
            Console.WriteLine("Test String: " + testString);
            Console.WriteLine("Result     : " + result);
            Console.ReadKey();
        }
    }
}

这会破坏有效的HTML。例如,它将&amp;转换为&amp;amp; - Dour High Arch
@NigelWhatling 做得非常好!唯一的缺陷是不支持的标签没有被转义(例如,<Invalid> 没有被转义)。 - Amir
@Amir 谢谢。在你编辑原始问题并添加了定义的标签集之前,我已经回答了。将正则表达式更改为仅捕获该标签集并转义其他所有内容并不难。 - Nigel Whatling
这是一个非常好的解决方案。今天我遇到了完全相同的情况,想让你知道它仍然有效。谢谢! - tnw

2
我个人认为这是不可能的,因为你真的在尝试修复格式错误的HTML,因此没有规则可用于确定什么需要编码,什么不需要。
无论如何,像<<Tag index="0" />这样的东西都不是有效的HTML。
如果你知道实际的标签,你可能能够创建一个白名单来简化事情,但你必须更具体地解决你的问题,我不认为你能够为任何场景解决这个问题。
事实上,很有可能你的文本中并没有任何随机的<>,这将(可能)极大地简化问题,但如果你真的想出一个通用的解决方案......祝你好运。

除非他已经允许仅使用一小组有效标签,否则这是不可能的。 - Bobson

1
这是一个正则表达式,您可以使用它来匹配任何无效的<>
(\<(?! ?/?(?:b|i|br|u|blink|flash|Tag[^>]*))|(?<! ?/?(?:b|i|br|u|blink|flash|Tag[^>]*))\>)

我建议将有效的标签测试表达式放入一个变量中,然后在其周围构建其余部分。
var validTags = "b|i|br|u|blink|flash|Tag[^>]*";
var startTag = @"\<(?! ?/?(?:" + validTags + "))";
var endTag = @"(?<! ?/?(?:" + validTags + "))/>";

然后只需对它们执行RegEx.Replace


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接