Add (de)serialization support for Base64 string as Byte array (dotnet/corefx#38106)
authorSteve Harter <steveharter@users.noreply.github.com>
Fri, 31 May 2019 18:55:12 +0000 (11:55 -0700)
committerAhson Khan <ahkha@microsoft.com>
Fri, 31 May 2019 18:55:12 +0000 (11:55 -0700)
* Add (de)serialization for Base64 string as Byte array

* Review feedback

Commit migrated from https://github.com/dotnet/corefx/commit/a7ad25c7749cbd8ede10119a2e0ab43276192d8e

src/libraries/System.Text.Json/src/System.Text.Json.csproj
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/DefaultConverters.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterByteArray.cs [new file with mode: 0644]
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonPropertyInfoCommon.cs
src/libraries/System.Text.Json/tests/Serialization/Array.ReadTests.cs
src/libraries/System.Text.Json/tests/Serialization/Array.WriteTests.cs
src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestClass.cs
src/libraries/System.Text.Json/tests/Serialization/TestClasses.SimpleTestStruct.cs

index 3fde4c3..3fc23f5 100644 (file)
@@ -57,6 +57,7 @@
     <Compile Include="System\Text\Json\Serialization\Converters\DefaultImmutableConverter.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterBoolean.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByte.cs" />
+    <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByteArray.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterChar.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDateTime.cs" />
     <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDateTimeOffset.cs" />
index bc33704..8bc855b 100644 (file)
@@ -11,9 +11,10 @@ namespace System.Text.Json.Serialization.Converters
     {
         private static readonly Dictionary<Type, object> s_valueConverters = new Dictionary<Type, object>()
         {
+            { typeof(byte[]), new JsonValueConverterByteArray() },
             { typeof(DateTimeOffset), new JsonValueConverterDateTimeOffset() },
             { typeof(Guid), new JsonValueConverterGuid() },
-            { typeof(JsonElement), new JsonValueConverterJsonElement() }
+            { typeof(JsonElement), new JsonValueConverterJsonElement() },
         };
 
         internal static bool IsValueConvertable(Type type)
@@ -32,6 +33,7 @@ namespace System.Text.Json.Serialization.Converters
                     new object[] { false },
                     culture: null);
             }
+
             TypeCode typeCode = Type.GetTypeCode(type);
             switch (typeCode)
             {
diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterByteArray.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonValueConverterByteArray.cs
new file mode 100644 (file)
index 0000000..b232293
--- /dev/null
@@ -0,0 +1,32 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+    internal sealed class JsonValueConverterByteArray : JsonValueConverter<byte[]>
+    {
+        public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out byte[] value)
+        {
+            if (reader.TokenType != JsonTokenType.String)
+            {
+                value = default;
+                return false;
+            }
+
+            return reader.TryGetBytesFromBase64(out value);
+        }
+
+        public override void Write(byte[] value, Utf8JsonWriter writer)
+        {
+            writer.WriteBase64StringValue(value);
+        }
+
+        public override void Write(JsonEncodedText propertyName, byte[] value, Utf8JsonWriter writer)
+        {
+            writer.WriteBase64String(propertyName, value);
+        }
+    }
+}
index 22be588..222d1cb 100644 (file)
@@ -50,7 +50,6 @@ namespace System.Text.Json.Serialization
                 IsPropertyPolicy = true;
                 HasGetter = true;
                 HasSetter = true;
-                ValueConverter = DefaultConverters<TRuntimeProperty>.s_converter;
             }
 
             GetPolicies();
index d87f133..aeddfbb 100644 (file)
@@ -26,6 +26,78 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
+        public static void ReadNullByteArray()
+        {
+            string json = @"null";
+            byte[] arr = JsonSerializer.Parse<byte[]>(json);
+            Assert.Null(arr);
+        }
+
+        [Fact]
+        public static void ReadEmptyByteArray()
+        {
+            string json = @"""""";
+            byte[] arr = JsonSerializer.Parse<byte[]>(json);
+            Assert.Equal(0, arr.Length);
+        }
+
+        [Fact]
+        public static void ReadByteArray()
+        {
+            string json = $"\"{Convert.ToBase64String(new byte[] { 1, 2 })}\"";
+            byte[] arr = JsonSerializer.Parse<byte[]>(json);
+
+            Assert.Equal(2, arr.Length);
+            Assert.Equal(1, arr[0]);
+            Assert.Equal(2, arr[1]);
+        }
+
+        [Fact]
+        public static void Read2dByteArray()
+        {
+            // Baseline for comparison.
+            Assert.Equal("AQI=", Convert.ToBase64String(new byte[] { 1, 2 }));
+
+            string json = "[\"AQI=\",\"AQI=\"]";
+            byte[][] arr = JsonSerializer.Parse<byte[][]>(json);
+            Assert.Equal(2, arr.Length);
+
+            Assert.Equal(2, arr[0].Length);
+            Assert.Equal(1, arr[0][0]);
+            Assert.Equal(2, arr[0][1]);
+
+            Assert.Equal(2, arr[1].Length);
+            Assert.Equal(1, arr[1][0]);
+            Assert.Equal(2, arr[1][1]);
+        }
+
+        [Fact]
+        public static void ReadByteArrayFail()
+        {
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<byte[]>(@"""1"""));
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<byte[]>(@"""A==="""));
+        }
+
+        [Fact]
+        public static void ReadByteArrayAsJsonArrayFail()
+        {
+            string json = $"[1, 2]";
+            // Currently no support deserializing JSON arrays as byte[] - only Base64 string.
+            Assert.Throws<JsonException>(() => JsonSerializer.Parse<byte[]>(json));
+        }
+
+        [Fact]
+        public static void ReadByteListAsJsonArray()
+        {
+            string json = $"[1, 2]";
+            List<byte> list = JsonSerializer.Parse<List<byte>>(json);
+
+            Assert.Equal(2, list.Count);
+            Assert.Equal(1, list[0]);
+            Assert.Equal(2, list[1]);
+        }
+
+        [Fact]
         public static void DeserializeObjectArray_36167()
         {
             // https://github.com/dotnet/corefx/issues/36167
index 65ef8bb..7af9123 100644 (file)
@@ -26,6 +26,39 @@ namespace System.Text.Json.Serialization.Tests
         }
 
         [Fact]
+        public static void WriteNullByteArray()
+        {
+            byte[] input = null;
+            string json = JsonSerializer.ToString(input);
+            Assert.Equal($"null", json);
+        }
+
+        [Fact]
+        public static void WriteEmptyByteArray()
+        {
+            var input = new byte[] {};
+            string json = JsonSerializer.ToString(input);
+            Assert.Equal(@"""""", json);
+        }
+
+        [Fact]
+        public static void WriteByteArray()
+        {
+            var input = new byte[] { 1, 2 };
+            string json = JsonSerializer.ToString(input);
+            Assert.Equal($"\"{Convert.ToBase64String(input)}\"", json);
+        }
+
+        [Fact]
+        public static void WriteTwo2dByteArray()
+        {
+            var inner = new byte[] { 1, 2 };
+            var outer = new byte[2][] { inner, inner };
+            string json = JsonSerializer.ToString(outer);
+            Assert.Equal($"[\"{Convert.ToBase64String(inner)}\",\"{Convert.ToBase64String(inner)}\"]", json);
+        }
+
+        [Fact]
         public static void WriteObjectArray()
         {
             string json;
index ba69ea6..31a28a5 100644 (file)
@@ -111,7 +111,7 @@ namespace System.Text.Json.Serialization.Tests
                 @"""MyUInt16Array"" : [4]," +
                 @"""MyUInt32Array"" : [5]," +
                 @"""MyUInt64Array"" : [6]," +
-                @"""MyByteArray"" : [7]," +
+                @"""MyByteArray"" : ""Bw==""," + // Base64 encoded value of 7
                 @"""MySByteArray"" : [8]," +
                 @"""MyCharArray"" : [""a""]," +
                 @"""MyStringArray"" : [""Hello""]," +
index a5fac97..3a40d7f 100644 (file)
@@ -83,7 +83,7 @@ namespace System.Text.Json.Serialization.Tests
                 @"""MyUInt16Array"" : [4]," +
                 @"""MyUInt32Array"" : [5]," +
                 @"""MyUInt64Array"" : [6]," +
-                @"""MyByteArray"" : [7]," +
+                @"""MyByteArray"" : ""Bw==""," + // Base64 encoded value of 7
                 @"""MySByteArray"" : [8]," +
                 @"""MyCharArray"" : [""a""]," +
                 @"""MyStringArray"" : [""Hello""]," +