mirror of
https://github.com/ckaczor/sqltoolsservice.git
synced 2026-02-17 02:51:45 -05:00
add flag converters nullable serialization (#857)
This commit is contained in:
committed by
Kevin Cunnane
parent
55c82fbcc4
commit
c6e3b33c35
@@ -0,0 +1,3 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.SqlTools.Hosting.UnitTests")]
|
||||||
@@ -26,15 +26,20 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
// TODO: Fix to handle nullables properly
|
var jToken = JToken.Load(reader);
|
||||||
|
if (jToken.Type == JTokenType.Null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int[] values = JArray.Load(reader).Values<int>().ToArray();
|
int[] values = ((JArray)jToken).Values<int>().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;
|
int setFlags = 0;
|
||||||
foreach (FieldInfo enumField in enumFields)
|
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
|
// If there is a serialize value set for the enum value, look for that instead of the int value
|
||||||
SerializeValueAttribute serializeValue = enumField.GetCustomAttribute<SerializeValueAttribute>();
|
SerializeValueAttribute serializeValue = enumField.GetCustomAttribute<SerializeValueAttribute>();
|
||||||
@@ -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)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
@@ -56,7 +61,7 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
{
|
{
|
||||||
// Make sure the flag is set before doing expensive reflection
|
// Make sure the flag is set before doing expensive reflection
|
||||||
int enumValue = (int)enumField.GetValue(null);
|
int enumValue = (int)enumField.GetValue(null);
|
||||||
if (((int) value & enumValue) == 0)
|
if (((int)value & enumValue) == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -71,7 +76,7 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
writer.WriteRawValue($"[{joinedFlags}]");
|
writer.WriteRawValue($"[{joinedFlags}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion Public Methods
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Field)]
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
internal class SerializeValueAttribute : Attribute
|
internal class SerializeValueAttribute : Attribute
|
||||||
|
|||||||
@@ -27,17 +27,16 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
|
|
||||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
{
|
{
|
||||||
// TODO: Fix to handle nullables properly
|
|
||||||
|
|
||||||
JToken jToken = JToken.Load(reader);
|
JToken jToken = JToken.Load(reader);
|
||||||
if (jToken.Type == JTokenType.Null)
|
if (jToken.Type == JTokenType.Null)
|
||||||
{
|
{
|
||||||
return 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;
|
int setFlags = 0;
|
||||||
foreach (FieldInfo enumField in enumFields)
|
foreach (FieldInfo enumField in enumFields)
|
||||||
{
|
{
|
||||||
@@ -52,11 +51,11 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
// If the value is in the json array, or the int value into the flags
|
// If the value is in the json array, or the int value into the flags
|
||||||
if (Array.IndexOf(values, searchValue) >= 0)
|
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)
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
@@ -66,8 +65,8 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
foreach (FieldInfo enumField in enumFields)
|
foreach (FieldInfo enumField in enumFields)
|
||||||
{
|
{
|
||||||
// Make sure the flag is set before doing any other work
|
// Make sure the flag is set before doing any other work
|
||||||
int enumValue = (int) enumField.GetValue(null);
|
int enumValue = (int)enumField.GetValue(null);
|
||||||
if (((int) value & enumValue) == 0)
|
if (((int)value & enumValue) == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -86,7 +85,7 @@ namespace Microsoft.SqlTools.DataProtocol.Contracts.Utilities
|
|||||||
writer.WriteRawValue($"[{joinedFlags}]");
|
writer.WriteRawValue($"[{joinedFlags}]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion Public Methods
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Field)]
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
internal class SerializeValueAttribute : Attribute
|
internal class SerializeValueAttribute : Attribute
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using Microsoft.SqlTools.DataProtocol.Contracts.Utilities;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.Hosting.UnitTests.Contracts.Utilities
|
||||||
|
{
|
||||||
|
public class FlagsIntConverterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void NullableValueCanBeDeserialized()
|
||||||
|
{
|
||||||
|
var jsonObject = JObject.Parse("{\"optionalValue\": [1, 2]}");
|
||||||
|
var contract = jsonObject.ToObject<DataContract>();
|
||||||
|
Assert.NotNull(contract);
|
||||||
|
Assert.NotNull(contract.OptionalValue);
|
||||||
|
Assert.Equal(TestFlags.FirstItem | TestFlags.SecondItem, contract.OptionalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegularValueCanBeDeserialized()
|
||||||
|
{
|
||||||
|
var jsonObject = JObject.Parse("{\"Value\": [1, 3]}");
|
||||||
|
var contract = jsonObject.ToObject<DataContract>();
|
||||||
|
Assert.NotNull(contract);
|
||||||
|
Assert.Equal(TestFlags.FirstItem | TestFlags.ThirdItem, contract.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExplicitNullCanBeDeserialized()
|
||||||
|
{
|
||||||
|
var jsonObject = JObject.Parse("{\"optionalValue\": null}");
|
||||||
|
var contract = jsonObject.ToObject<DataContract>();
|
||||||
|
Assert.NotNull(contract);
|
||||||
|
Assert.Null(contract.OptionalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
[JsonConverter(typeof(FlagsIntConverter))]
|
||||||
|
private enum TestFlags
|
||||||
|
{
|
||||||
|
[FlagsIntConverter.SerializeValue(1)]
|
||||||
|
FirstItem = 1 << 0,
|
||||||
|
|
||||||
|
[FlagsIntConverter.SerializeValue(2)]
|
||||||
|
SecondItem = 1 << 1,
|
||||||
|
|
||||||
|
[FlagsIntConverter.SerializeValue(3)]
|
||||||
|
ThirdItem = 1 << 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DataContract
|
||||||
|
{
|
||||||
|
public TestFlags? OptionalValue { get; set; }
|
||||||
|
|
||||||
|
public TestFlags Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// Copyright (c) Microsoft. All rights reserved.
|
||||||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.SqlTools.DataProtocol.Contracts.Utilities;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.SqlTools.Hosting.UnitTests.Contracts.Utilities
|
||||||
|
{
|
||||||
|
public class FlagsStringConverterTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void NullableValueCanBeDeserialized()
|
||||||
|
{
|
||||||
|
var jsonObject = JObject.Parse("{\"optionalValue\": [\"First\", \"Second\"]}");
|
||||||
|
var contract = jsonObject.ToObject<DataContract>();
|
||||||
|
Assert.NotNull(contract);
|
||||||
|
Assert.NotNull(contract.OptionalValue);
|
||||||
|
Assert.Equal(TestFlags.FirstItem | TestFlags.SecondItem, contract.OptionalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RegularValueCanBeDeserialized()
|
||||||
|
{
|
||||||
|
var jsonObject = JObject.Parse("{\"Value\": [\"First\", \"Third\"]}");
|
||||||
|
var contract = jsonObject.ToObject<DataContract>();
|
||||||
|
Assert.NotNull(contract);
|
||||||
|
Assert.Equal(TestFlags.FirstItem | TestFlags.ThirdItem, contract.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ExplicitNullCanBeDeserialized()
|
||||||
|
{
|
||||||
|
var jsonObject = JObject.Parse("{\"optionalValue\": null}");
|
||||||
|
var contract = jsonObject.ToObject<DataContract>();
|
||||||
|
Assert.NotNull(contract);
|
||||||
|
Assert.Null(contract.OptionalValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Flags]
|
||||||
|
[JsonConverter(typeof(FlagsStringConverter))]
|
||||||
|
private enum TestFlags
|
||||||
|
{
|
||||||
|
[FlagsStringConverter.SerializeValue("First")]
|
||||||
|
FirstItem = 1 << 0,
|
||||||
|
|
||||||
|
[FlagsStringConverter.SerializeValue("Second")]
|
||||||
|
SecondItem = 1 << 1,
|
||||||
|
|
||||||
|
[FlagsStringConverter.SerializeValue("Third")]
|
||||||
|
ThirdItem = 1 << 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DataContract
|
||||||
|
{
|
||||||
|
public TestFlags? OptionalValue { get; set; }
|
||||||
|
|
||||||
|
public TestFlags Value { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user