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("GG;", "GG;")]
+ [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));
+ }
+ }
+}