Skip to content

Commit 4073f3e

Browse files
grendellojonpryor
authored andcommitted
[generator] Fix loading broken annotation XML (dotnet#415)
Context: https://issuetracker.google.com/issues/116182838 Google shipped invalid (not well-formed) XML annotation documents in the `platform-tools` 28.0.1 package, with `<` present within element attributes: <item name="android.view.View void addFocusables(java.util.ArrayList<android.view.View>, int) 1"> Note the `java.util.ArrayList<android.view.View>` within the `//item/@name` attribute value, and that the `<` isn't escaped. This invalid XML prevents System.Xml from loading the `annotations.xml` documents, with errors similar to: android/view/annotations.xml failed: '<', hexadecimal value 0x3C, is an invalid attribute character. Line 206, position 71 This commit takes advantage of the forgiving nature of the HtmlAgilityPack HTML parser -- which loads those files fine -- and makes it possible for us to write them back using `XmlWriter` in order to generate a well-formed XML document which we can load into `XDocument`. Slow, but it works...
1 parent 3a99c2f commit 4073f3e

File tree

3 files changed

+67
-1
lines changed

3 files changed

+67
-1
lines changed

src/Xamarin.Android.Tools.AnnotationSupport/AndroidAnnotationsSupport.cs

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33
using System.IO;
44
using System.IO.Compression;
55
using System.Linq;
6+
using System.Text;
67
using System.Xml.Linq;
8+
using System.Xml;
9+
using HtmlAgilityPack;
710

811
namespace Xamarin.AndroidTools.AnnotationSupport
912
{
@@ -24,7 +27,63 @@ public static IList<AnnotatedItem> ParseArchive (string file)
2427
static IEnumerable<AnnotatedItem> ParseArchiveEntry (ZipArchiveEntry entry)
2528
{
2629
using (var s = entry.Open ())
27-
return XDocument.Load (s).Root.Elements ("item").Select (e => new AnnotatedItem (e));
30+
return SafeXmlLoad (s, entry.FullName).Root.Elements ("item").Select (e => new AnnotatedItem (e));
31+
}
32+
33+
static XDocument SafeXmlLoad (Stream s, string fileName)
34+
{
35+
// We must save to a temporary stream because the stream doesn't support seeking and should the
36+
// parsing fail we won't be able to go back to its beginnig to reparse it.
37+
using (var ms = new MemoryStream ()) {
38+
s.CopyTo (ms);
39+
ms.Seek (0, SeekOrigin.Begin);
40+
41+
try {
42+
return XDocument.Load (ms);
43+
} catch (XmlException ex) {
44+
Console.Error.WriteLine ($"Warning: failed to load annotation document '{fileName}' directly from the annotations archive. {ex.Message}");
45+
Console.Error.WriteLine ("Attempting to fix up and reload");
46+
}
47+
48+
try {
49+
using (var ns = FixAnnotationXML (ms)) {
50+
return XDocument.Load (ns);
51+
}
52+
} catch (Exception ex) {
53+
throw new InvalidOperationException ($"Failed to fix up invalid XML in annotation document '{fileName}'. {ex.Message}", ex);
54+
}
55+
}
56+
}
57+
58+
static Stream FixAnnotationXML (Stream s)
59+
{
60+
s.Seek (0, SeekOrigin.Begin);
61+
62+
//
63+
// Context: https://issuetracker.google.com/issues/116182838
64+
//
65+
// Google ships not well-formed XML files in the platform-tools 28.0.1 package (in the
66+
// annotations.zip file), so we need to load the files with a forgiving parser in order to fix
67+
// them up before loading with a validating XML parser.
68+
var doc = new HtmlDocument ();
69+
doc.Load (s);
70+
if (doc.DocumentNode.FirstChild.InnerHtml.StartsWith ("<?xml"))
71+
doc.DocumentNode.FirstChild.Remove ();
72+
73+
var ms = new MemoryStream ();
74+
var xs = new XmlWriterSettings {
75+
Encoding = new UTF8Encoding (false),
76+
CloseOutput = false,
77+
ConformanceLevel = ConformanceLevel.Fragment,
78+
};
79+
80+
using (var xw = XmlWriter.Create (ms, xs)) {
81+
doc.Save (xw);
82+
xw.Flush ();
83+
}
84+
85+
ms.Seek (0, SeekOrigin.Begin);
86+
return ms;
2887
}
2988

3089
#endregion

src/Xamarin.Android.Tools.AnnotationSupport/Xamarin.Android.Tools.AnnotationSupport.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
<Reference Include="System.IO.Compression" />
3636
<Reference Include="System.Xml" />
3737
<Reference Include="System.Xml.Linq" />
38+
<Reference Include="HtmlAgilityPack">
39+
<HintPath>..\..\packages\HtmlAgilityPack.1.8.14\lib\Net45\HtmlAgilityPack.dll</HintPath>
40+
</Reference>
3841
</ItemGroup>
3942
<ItemGroup>
4043
<Compile Include="AndroidAnnotationsSupport.cs" />
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<packages>
3+
<package id="HtmlAgilityPack" version="1.8.14" targetFramework="net45" />
4+
</packages>

0 commit comments

Comments
 (0)