diff --git a/src/HtmlAgilityPack.Shared/HtmlEntity.cs b/src/HtmlAgilityPack.Shared/HtmlEntity.cs index 52cea1b..261701f 100644 --- a/src/HtmlAgilityPack.Shared/HtmlEntity.cs +++ b/src/HtmlAgilityPack.Shared/HtmlEntity.cs @@ -663,7 +663,8 @@ namespace HtmlAgilityPack } catch { - sb.Append("&#" + e + ";"); + // 'e' already starts with '#', so prepending "&#" would duplicate it. + sb.Append("&" + e + ";"); } } else diff --git a/src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs b/src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs new file mode 100644 index 0000000..52535c2 --- /dev/null +++ b/src/Tests/HtmlAgilityPack.Tests.NetStandard2_0/HtmlEntityDeEntitizeTests.cs @@ -0,0 +1,25 @@ +using Xunit; + +namespace HtmlAgilityPack.Tests.NetStandard2_0 +{ + public class HtmlEntityDeEntitizeTests + { + [Theory] + // Invalid numeric entities must be left untouched; "&#foo;" used to become + // "&##foo;". + [InlineData("&#foo;", "&#foo;")] + [InlineData("&#xGG;", "&#xGG;")] + [InlineData("&#;", "&#;")] + [InlineData("prefix &#foo; suffix", "prefix &#foo; suffix")] + // Valid numeric entities keep decoding correctly. + [InlineData("A", "A")] + [InlineData("A", "A")] + // Named entities are unaffected. + [InlineData("&", "&")] + [InlineData("¬real;", "¬real;")] + public void DeEntitize_HandlesInvalidNumericEntitiesWithoutDuplicatingHash(string input, string expected) + { + Assert.Equal(expected, HtmlEntity.DeEntitize(input)); + } + } +}