add flag converters nullable serialization (#857)

This commit is contained in:
sergei-boguslavski
2019-08-29 10:35:45 -07:00
committed by Kevin Cunnane
parent 55c82fbcc4
commit c6e3b33c35
6 changed files with 205 additions and 32 deletions

View File

@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.SqlTools.Hosting.UnitTests")]

View File

@@ -18,7 +18,7 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
public override bool CanRead => true;
#region Public Methods
public override bool CanConvert(Type objectType)
{
return objectType.IsEnum && objectType.GetCustomAttribute(typeof(FlagsAttribute)) != null;
@@ -26,16 +26,21 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// TODO: Fix to handle nullables properly
int[] values = JArray.Load(reader).Values<int>().ToArray();
var jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.Null)
{
return null;
}
FieldInfo[] enumFields = objectType.GetFields(BindingFlags.Public | BindingFlags.Static);
int[] values = ((JArray)jToken).Values<int>().ToArray();
var pureType = NullableUtils.GetUnderlyingTypeIfNullable(objectType);
FieldInfo[] enumFields = pureType.GetFields(BindingFlags.Public | BindingFlags.Static);
int setFlags = 0;
foreach (FieldInfo enumField in enumFields)
{
int enumValue = (int) enumField.GetValue(null);
int enumValue = (int)enumField.GetValue(null);
// If there is a serialize value set for the enum value, look for that instead of the int value
SerializeValueAttribute serializeValue = enumField.GetCustomAttribute<SerializeValueAttribute>();
int searchValue = serializeValue?.Value ?? enumValue;
@@ -45,7 +50,7 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
}
}
return Enum.ToObject(objectType, setFlags);
return Enum.ToObject(pureType, setFlags);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
@@ -56,22 +61,22 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
{
// Make sure the flag is set before doing expensive reflection
int enumValue = (int)enumField.GetValue(null);
if (((int) value & enumValue) == 0)
if (((int)value & enumValue) == 0)
{
continue;
}
// If there is a serialize value set for the member, use that instead of the int value
SerializeValueAttribute serializeValue = enumField.GetCustomAttribute<SerializeValueAttribute>();
int flagValue = serializeValue?.Value ?? enumValue;
int flagValue = serializeValue?.Value ?? enumValue;
setFlags.Add(flagValue);
}
string joinedFlags = string.Join(", ", setFlags);
writer.WriteRawValue($"[{joinedFlags}]");
}
#endregion
#endregion Public Methods
[AttributeUsage(AttributeTargets.Field)]
internal class SerializeValueAttribute : Attribute
@@ -82,6 +87,6 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
{
Value = value;
}
}
}
}
}

View File

@@ -14,33 +14,32 @@ using Newtonsoft.Json.Serialization;
namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
{
internal class FlagsStringConverter : JsonConverter
{
{
public override bool CanWrite => true;
public override bool CanRead => true;
#region Public Methods
public override bool CanConvert(Type objectType)
{
return objectType.IsEnum && objectType.GetCustomAttribute(typeof(FlagsAttribute)) != null;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// TODO: Fix to handle nullables properly
JToken jToken = JToken.Load(reader);
if (jToken.Type == JTokenType.Null)
{
return null;
}
string[] values = ((JArray) jToken).Values<string>().ToArray();
string[] values = ((JArray)jToken).Values<string>().ToArray();
var pureType = NullableUtils.GetUnderlyingTypeIfNullable(objectType);
FieldInfo[] enumFields = objectType.GetFields(BindingFlags.Public | BindingFlags.Static);
FieldInfo[] enumFields = pureType.GetFields(BindingFlags.Public | BindingFlags.Static);
int setFlags = 0;
foreach (FieldInfo enumField in enumFields)
{
{
// If there is a serialize value set for the enum value, look for the instead of the int value
SerializeValueAttribute serializeValue = enumField.GetCustomAttribute<SerializeValueAttribute>();
string searchValue = serializeValue?.Value ?? enumField.Name;
@@ -48,15 +47,15 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
{
searchValue = char.ToLowerInvariant(searchValue[0]) + searchValue.Substring(1);
}
// If the value is in the json array, or the int value into the flags
if (Array.IndexOf(values, searchValue) >= 0)
{
setFlags |= (int) enumField.GetValue(null);
setFlags |= (int)enumField.GetValue(null);
}
}
return Enum.ToObject(objectType, setFlags);
return Enum.ToObject(pureType, setFlags);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
@@ -66,12 +65,12 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
foreach (FieldInfo enumField in enumFields)
{
// Make sure the flag is set before doing any other work
int enumValue = (int) enumField.GetValue(null);
if (((int) value & enumValue) == 0)
int enumValue = (int)enumField.GetValue(null);
if (((int)value & enumValue) == 0)
{
continue;
}
// If there is a serialize value set for the member, use that instead of the int value
SerializeValueAttribute serializeValue = enumField.GetCustomAttribute<SerializeValueAttribute>();
string flagValue = serializeValue?.Value ?? enumField.Name;
@@ -85,9 +84,9 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
string joinedFlags = string.Join(", ", setFlags);
writer.WriteRawValue($"[{joinedFlags}]");
}
#endregion
#endregion Public Methods
[AttributeUsage(AttributeTargets.Field)]
internal class SerializeValueAttribute : Attribute
{

View File

@@ -0,0 +1,36 @@
//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
using System;
namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
{
internal static class NullableUtils
{
/// <summary>
/// Determines whether the type is <see cref="Nullable{T}"/>.
/// </summary>
/// <param name="t">The type.</param>
/// <returns>
/// <c>true</c> if <paramref name="t"/> is <see cref="Nullable{T}"/>; otherwise, <c>false</c>.
/// </returns>
public static bool IsNullable(Type t)
{
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
/// <summary>
/// Unwraps the <see cref="Nullable{T}"/> if necessary and returns the underlying value type.
/// </summary>
/// <param name="t">The type.</param>
/// <returns>The underlying value type the <see cref="Nullable{T}"/> type was produced from,
/// or the <paramref name="t"/> type if the type is not <see cref="Nullable{T}"/>.
/// </returns>
public static Type GetUnderlyingTypeIfNullable(Type t)
{
return IsNullable(t) ? Nullable.GetUnderlyingType(t) : t;
}
}
}