From 30bdef0befd2d8c1b149c8b7b59796488ff4f637 Mon Sep 17 00:00:00 2001
From: rlittlesii <6969701+RLittlesII@users.noreply.github.com>
Date: Sat, 26 Mar 2022 13:24:53 -0500
Subject: [PATCH] enhancement: fix issue with Plist writing additional
 characters to info.plist during serialization

---
 src/Nuke/Xamarin/Plist.cs | 149 ++++++++++++++++----------------------
 1 file changed, 63 insertions(+), 86 deletions(-)

diff --git a/src/Nuke/Xamarin/Plist.cs b/src/Nuke/Xamarin/Plist.cs
index abbe5039..2ed7d315 100644
--- a/src/Nuke/Xamarin/Plist.cs
+++ b/src/Nuke/Xamarin/Plist.cs
@@ -1,12 +1,13 @@
-using System.Globalization;
-using System.Text;
+using System.Collections;
+using System.Globalization;
 using System.Xml.Linq;
 using Nuke.Common.IO;
+using Serilog;
 
 namespace Rocket.Surgery.Nuke.Xamarin;
 
 /// <summary>
-///     Taken from https://github.com/cake-contrib/Cake.Plist/blob/develop/src/Cake.Plist/PlistConverter.cs
+///     Taken from https://github.com/cake-contrib/Cake.Plist/blob/develop/src/Cake.Plist/PlistConverter.cs.
 /// </summary>
 internal static class Plist
 {
@@ -28,36 +29,17 @@ public static dynamic Deserialize(AbsolutePath plist)
     /// </summary>
     /// <param name="path">The path to the plist.</param>
     /// <param name="value">The object to serialize.</param>
-    /// <returns>The deserialized plist.</returns>
     public static void Serialize(AbsolutePath path, object value)
     {
-        var doc = SerializeDocument(value);
-
-        string result;
-
-        using (var sw = new MemoryStream())
-        {
-            using (var strw = new StreamWriter(sw))
-            {
-                doc.Save(strw);
-                result = new UTF8Encoding(false).GetString(sw.ToArray());
-            }
-        }
-
-        using (var stream = File.OpenWrite(path))
-        {
-            using (var write = new StreamWriter(stream, new UTF8Encoding(false), 1024, true))
-            {
-                write.Write(result);
-            }
-        }
+        SerializeDocument(value)
+           .Save(path, SaveOptions.OmitDuplicateNamespaces);
     }
 
     /// <summary>
     ///     Serializes the .plist file provided.
     /// </summary>
-    /// <param name="item">The plist object</param>
-    /// <returns>The xml document</returns>
+    /// <param name="item">The plist object.</param>
+    /// <returns>The xml document.</returns>
     private static XDocument SerializeDocument(object item)
     {
         var doc = new XDocument(new XDeclaration("1.0", "UTF-8", "yes"));
@@ -66,7 +48,7 @@ private static XDocument SerializeDocument(object item)
                 "plist",
                 "-//Apple//DTD PLIST 1.0//EN",
                 "http://www.apple.com/DTDs/PropertyList-1.0.dtd",
-                ""
+                string.Empty
             )
         );
 
@@ -81,78 +63,71 @@ private static XDocument SerializeDocument(object item)
     /// <summary>
     ///     Serializes the .plist file provided.
     /// </summary>
-    /// <param name="item">The plist object</param>
-    /// <returns>The xml element</returns>
+    /// <param name="item">The plist object.</param>
+    /// <returns>The xml element.</returns>
     private static XElement? SerializeObject(object item)
     {
-        if (item is string)
+        switch (item)
         {
-            return new XElement("string", item);
-        }
-
-        if (item is double || item is float || item is decimal)
-        {
-            return new XElement("real", Convert.ToString(item, CultureInfo.InvariantCulture));
-        }
-
-        if (item is int || item is long)
-        {
-            return new XElement("integer", Convert.ToString(item, CultureInfo.InvariantCulture));
-        }
-
-        if (item is bool && item as bool? == true)
-        {
-            return new XElement("true");
-        }
-
-        if (item is bool && item as bool? == false)
-        {
-            return new XElement("false");
-        }
-
-        if (item is DateTime time)
-        {
-            return new XElement("date", time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", DateTimeFormatInfo.InvariantInfo));
-        }
-
-        if (item is DateTimeOffset offset)
-        {
-            return new XElement("date", offset.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ", DateTimeFormatInfo.InvariantInfo));
-        }
-
-        if (item is byte[] bytes)
-        {
-            return new XElement("data", Convert.ToBase64String(bytes));
-        }
+            case string:
+                Log.Verbose("string: {String}", item);
+                return new XElement("string", item);
+            case double:
+            case float:
+            case decimal:
+                Log.Verbose("floating point: {Float}", item);
+                return new XElement("real", Convert.ToString(item, CultureInfo.InvariantCulture));
+            case int:
+            case long:
+                Log.Verbose("integer: {Integer}", item);
+                return new XElement("integer", Convert.ToString(item, CultureInfo.InvariantCulture));
+            case bool when item as bool? == true:
+                Log.Verbose("boolean: {Boolean}", item);
+                return new XElement("true");
+            case bool when item as bool? == false:
+                Log.Verbose("boolean: {Boolean}", item);
+                return new XElement("false");
+            case DateTime time:
+                Log.Verbose("DateTime: {DateTime}", item);
+                return new XElement("date", time.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", DateTimeFormatInfo.InvariantInfo));
+            case DateTimeOffset offset:
+                Log.Verbose("DateTimeOffset: {DateTimeOffset}", item);
+                return new XElement("date", offset.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ", DateTimeFormatInfo.InvariantInfo));
+            case byte[] bytes:
+                Log.Verbose("DateTimeOffset: {DateTimeOffset}", item);
+                return new XElement("data", Convert.ToBase64String(bytes));
+            case IDictionary dictionary:
+            {
+                var dict = new XElement("dict");
 
-        if (item is System.Collections.IDictionary dictionary)
-        {
-            var dict = new XElement("dict");
+                var enumerator = dictionary.GetEnumerator();
 
-            var enumerator = dictionary.GetEnumerator();
+                while (enumerator.MoveNext())
+                {
+                    dict.Add(new XElement("key", enumerator.Key));
+                    dict.Add(SerializeObject(enumerator.Value!));
+                }
 
-            while (enumerator.MoveNext())
-            {
-                dict.Add(new XElement("key", enumerator.Key));
-                dict.Add(SerializeObject(enumerator.Value!));
+                Log.Verbose("Dictionary: {Dictionary}", item);
+                return dict;
             }
 
-            return dict;
-        }
+            case IEnumerable enumerable:
+            {
+                var array = new XElement("array");
 
-        if (item is System.Collections.IEnumerable enumerable)
-        {
-            var array = new XElement("array");
+                foreach (var itm in enumerable)
+                {
+                    array.Add(SerializeObject(itm!));
+                }
 
-            foreach (var itm in enumerable)
-            {
-                array.Add(SerializeObject(itm!));
+                Log.Verbose("Array: {Array}", item);
+                return array;
             }
 
-            return array;
+            default:
+                return null;
         }
-
-        return null;
     }
 
     private static dynamic DeserializeXml(XElement element)
@@ -195,6 +170,7 @@ private static dynamic DeserializeXml(XElement element)
 
                 return typedArray;
             }
+
             case "dict":
             {
                 var dictionary = new Dictionary<string, object>();
@@ -217,6 +193,7 @@ private static dynamic DeserializeXml(XElement element)
 
                 return dictionary;
             }
+
             default:
                 return null!;
         }