Skip to content

Commit b8b8cc2

Browse files
committed
Improved self-closing / empty serialization #11
1 parent 73a370a commit b8b8cc2

File tree

4 files changed

+70
-5
lines changed

4 files changed

+70
-5
lines changed

src/AngleSharp.Xml.Tests/Various.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace AngleSharp.Xml.Tests
1+
namespace AngleSharp.Xml.Tests
22
{
33
using AngleSharp.Dom;
44
using AngleSharp.Io;
@@ -63,6 +63,40 @@ public async Task GenerateDocumentFromSvgWithSvgContentType()
6363
Assert.AreEqual("path", document.DocumentElement.FirstElementChild.NodeName);
6464
}
6565

66+
[Test]
67+
public void SelfClosingTagsAreSerializedCorrectlyByDefaultFormatter_Issue11()
68+
{
69+
var parser = new XmlParser();
70+
var xmlDoc = parser.ParseDocument(
71+
@"<Project Sdk=""Microsoft.NET.Sdk"">
72+
<ItemGroup>
73+
<PackageReference Include=""AngleSharp"" Version=""0.12.1"" />
74+
<PackageReference></PackageReference>
75+
</ItemGroup>
76+
</Project>");
77+
var xml = xmlDoc.ToXml();
78+
Assert.AreEqual("<Project Sdk=\"Microsoft.NET.Sdk\">\n <ItemGroup>\n <PackageReference Include=\"AngleSharp\" Version=\"0.12.1\" />\n <PackageReference></PackageReference>\n </ItemGroup>\n </Project>", xml);
79+
}
80+
81+
[Test]
82+
public void EmptyTagsAreSerializedCorrectlyWithStandardFormatterWithOption_Issue11()
83+
{
84+
var parser = new XmlParser();
85+
var xmlDoc = parser.ParseDocument(
86+
@"<Project Sdk=""Microsoft.NET.Sdk"">
87+
<ItemGroup>
88+
<PackageReference Include=""AngleSharp"" Version=""0.12.1"" />
89+
<PackageReference></PackageReference>
90+
</ItemGroup>
91+
</Project>");
92+
var formatter = new XmlMarkupFormatter
93+
{
94+
IsAlwaysSelfClosing = true,
95+
};
96+
var xml = xmlDoc.ToHtml(formatter);
97+
Assert.AreEqual("<Project Sdk=\"Microsoft.NET.Sdk\">\n <ItemGroup>\n <PackageReference Include=\"AngleSharp\" Version=\"0.12.1\" />\n <PackageReference />\n </ItemGroup>\n </Project>", xml);
98+
}
99+
66100
private static Task<IDocument> GenerateDocument(String content, String contentType)
67101
{
68102
var config = Configuration.Default.WithDefaultLoader().WithXml();

src/AngleSharp.Xml/MarkupFormatterExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace AngleSharp.Xml
22
{
33
using System;
4+
using System.IO;
45

56
/// <summary>
67
/// Extensions for the markup formattable elements.
@@ -16,6 +17,14 @@ public static class MarkupFormatterExtensions
1617
public static String ToXml(this IMarkupFormattable markup) =>
1718
markup.ToHtml(XmlMarkupFormatter.Instance);
1819

20+
/// <summary>
21+
/// Serializes the object model guided by the XML markup formatter.
22+
/// </summary>
23+
/// <param name="markup">The markup to serialize.</param>
24+
/// <param name="writer">The output target of the serialization.</param>
25+
public static void ToXml(this IMarkupFormattable markup, TextWriter writer) =>
26+
markup.ToHtml(writer, XmlMarkupFormatter.Instance);
27+
1928
/// <summary>
2029
/// Returns the serialization of the object model guided by the
2130
/// auto selected (XML, XHTML, HTML) markup formatter.
@@ -24,5 +33,14 @@ public static String ToXml(this IMarkupFormattable markup) =>
2433
/// <returns>The source code snippet.</returns>
2534
public static String ToMarkup(this IMarkupFormattable markup) =>
2635
markup.ToHtml(new AutoSelectedMarkupFormatter());
36+
37+
/// <summary>
38+
/// Serializes the object model guided by the auto selected (XML,
39+
/// XHTML, HTML) markup formatter.
40+
/// </summary>
41+
/// <param name="markup">The markup to serialize.</param>
42+
/// <param name="writer">The output target of the serialization.</param>
43+
public static void ToMarkup(this IMarkupFormattable markup, TextWriter writer) =>
44+
markup.ToHtml(writer, new AutoSelectedMarkupFormatter());
2745
}
2846
}

src/AngleSharp.Xml/Parser/XmlDomBuilder.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ private void InBody(XmlToken token)
271271
case XmlTokenType.StartTag:
272272
{
273273
var tagToken = (XmlTagToken)token;
274-
var element = CreateElement(tagToken.Name);
274+
var element = CreateElement(tagToken.Name, tagToken.IsSelfClosing);
275275
CurrentNode.AppendChild(element);
276276

277277
for (var i = 0; i < tagToken.Attributes.Count; i++)
@@ -402,17 +402,19 @@ private void AfterBody(XmlToken token)
402402

403403
#region Helpers
404404

405-
private Element CreateElement(String name)
405+
private Element CreateElement(String name, Boolean selfClosing)
406406
{
407407
var prefix = default(String);
408408
var colon = name.IndexOf(Symbols.Colon);
409+
var flags = selfClosing ? NodeFlags.SelfClosing : NodeFlags.None;
409410

410411
if (colon > 0 && colon < name.Length - 1)
411412
{
412413
prefix = name.Substring(0, colon);
413414
name = name.Substring(colon + 1);
414415
}
415416

417+
//TODO transport flags
416418
return _document.CreateElementFrom(name, prefix);
417419
}
418420

src/AngleSharp.Xml/XmlMarkupFormatter.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,25 @@ public sealed class XmlMarkupFormatter : IMarkupFormatter
1818

1919
#endregion
2020

21+
#region Properties
22+
23+
/// <summary>
24+
/// Gets or sets if empty elements should always be considered self-closing.
25+
/// Otherwise, requires the element to have a self-closing flag.
26+
/// </summary>
27+
public Boolean IsAlwaysSelfClosing { get; set; }
28+
29+
#endregion
30+
2131
#region Methods
2232

2333
String IMarkupFormatter.CloseTag(IElement element, Boolean selfClosing)
2434
{
2535
var prefix = element.Prefix;
2636
var name = element.LocalName;
2737
var tag = !String.IsNullOrEmpty(prefix) ? String.Concat(prefix, ":", name) : name;
28-
return selfClosing ? String.Empty : String.Concat("</", tag, ">");
38+
var closed = selfClosing || IsAlwaysSelfClosing && !element.HasChildNodes;
39+
return closed ? String.Empty : String.Concat("</", tag, ">");
2940
}
3041

3142
String IMarkupFormatter.Comment(IComment comment) => String.Concat("<!--", comment.Data, "-->");
@@ -59,7 +70,7 @@ String IMarkupFormatter.OpenTag(IElement element, Boolean selfClosing)
5970
temp.Append(" ").Append(Instance.Attribute(attribute));
6071
}
6172

62-
if (selfClosing)
73+
if (selfClosing || (IsAlwaysSelfClosing && !element.HasChildNodes))
6374
{
6475
temp.Append(" /");
6576
}

0 commit comments

Comments
 (0)