From 2af90445216461902ed641fb15bde5e9e109d419 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sun, 11 Feb 2018 21:45:06 +1000 Subject: [PATCH 01/12] add deserializer tests for nullable enums, StringEnum and nullable StringEnum properties --- Octokit.Tests/SimpleJsonSerializerTests.cs | 80 +++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/Octokit.Tests/SimpleJsonSerializerTests.cs b/Octokit.Tests/SimpleJsonSerializerTests.cs index 7194cd352e..37177128f2 100644 --- a/Octokit.Tests/SimpleJsonSerializerTests.cs +++ b/Octokit.Tests/SimpleJsonSerializerTests.cs @@ -301,7 +301,7 @@ public void DefaultsMissingParameters() } [Fact] - public void DeserializesEnumWithParameterAttribute() + public void DeserializesEnums() { const string json1 = @"{""some_enum"":""+1""}"; const string json2 = @"{""some_enum"":""utf-8""}"; @@ -322,6 +322,78 @@ public void DeserializesEnumWithParameterAttribute() Assert.Equal(SomeEnum.Unicode, sample5.SomeEnum); } + [Fact] + public void DeserializesNullableEnums() + { + const string json1 = @"{""some_enum_nullable"":""+1""}"; + const string json2 = @"{""some_enum_nullable"":""utf-8""}"; + const string json3 = @"{""some_enum_nullable"":""something else""}"; + const string json4 = @"{""some_enum_nullable"":""another_example""}"; + const string json5 = @"{""some_enum_nullable"":""unicode""}"; + const string json6 = @"{""some_enum_nullable"":null}"; + + var sample1 = new SimpleJsonSerializer().Deserialize(json1); + var sample2 = new SimpleJsonSerializer().Deserialize(json2); + var sample3 = new SimpleJsonSerializer().Deserialize(json3); + var sample4 = new SimpleJsonSerializer().Deserialize(json4); + var sample5 = new SimpleJsonSerializer().Deserialize(json5); + var sample6 = new SimpleJsonSerializer().Deserialize(json6); + + Assert.Equal(SomeEnum.PlusOne, sample1.SomeEnumNullable); + Assert.Equal(SomeEnum.Utf8, sample2.SomeEnumNullable); + Assert.Equal(SomeEnum.SomethingElse, sample3.SomeEnumNullable); + Assert.Equal(SomeEnum.AnotherExample, sample4.SomeEnumNullable); + Assert.Equal(SomeEnum.Unicode, sample5.SomeEnumNullable); + Assert.False(sample6.SomeEnumNullable.HasValue); + } + + [Fact] + public void DeserializesStringEnums() + { + const string json1 = @"{""string_enum"":""+1""}"; + const string json2 = @"{""string_enum"":""utf-8""}"; + const string json3 = @"{""string_enum"":""something else""}"; + const string json4 = @"{""string_enum"":""another_example""}"; + const string json5 = @"{""string_enum"":""unicode""}"; + + var sample1 = new SimpleJsonSerializer().Deserialize(json1); + var sample2 = new SimpleJsonSerializer().Deserialize(json2); + var sample3 = new SimpleJsonSerializer().Deserialize(json3); + var sample4 = new SimpleJsonSerializer().Deserialize(json4); + var sample5 = new SimpleJsonSerializer().Deserialize(json5); + + Assert.Equal(SomeEnum.PlusOne, sample1.StringEnum); + Assert.Equal(SomeEnum.Utf8, sample2.StringEnum); + Assert.Equal(SomeEnum.SomethingElse, sample3.StringEnum); + Assert.Equal(SomeEnum.AnotherExample, sample4.StringEnum); + Assert.Equal(SomeEnum.Unicode, sample5.StringEnum); + } + + [Fact] + public void DeserializesNullableStringEnums() + { + const string json1 = @"{""string_enum_nullable"":""+1""}"; + const string json2 = @"{""string_enum_nullable"":""utf-8""}"; + const string json3 = @"{""string_enum_nullable"":""something else""}"; + const string json4 = @"{""string_enum_nullable"":""another_example""}"; + const string json5 = @"{""string_enum_nullable"":""unicode""}"; + const string json6 = @"{""string_enum_nullable"":null}"; + + var sample1 = new SimpleJsonSerializer().Deserialize(json1); + var sample2 = new SimpleJsonSerializer().Deserialize(json2); + var sample3 = new SimpleJsonSerializer().Deserialize(json3); + var sample4 = new SimpleJsonSerializer().Deserialize(json4); + var sample5 = new SimpleJsonSerializer().Deserialize(json5); + var sample6 = new SimpleJsonSerializer().Deserialize(json6); + + Assert.Equal(SomeEnum.PlusOne, sample1.StringEnumNullable); + Assert.Equal(SomeEnum.Utf8, sample2.StringEnumNullable); + Assert.Equal(SomeEnum.SomethingElse, sample3.StringEnumNullable); + Assert.Equal(SomeEnum.AnotherExample, sample4.StringEnumNullable); + Assert.Equal(SomeEnum.Unicode, sample5.StringEnumNullable); + Assert.False(sample6.StringEnumNullable.HasValue); + } + [Fact] public void ShouldDeserializeMultipleEnumValues() { @@ -369,6 +441,12 @@ public class ObjectWithEnumProperty public string Name { get; set; } public SomeEnum SomeEnum { get; set; } + + public SomeEnum? SomeEnumNullable { get; set; } + + public StringEnum StringEnum { get; set; } + + public StringEnum? StringEnumNullable { get; set; } } public enum SomeEnum From 5fa99754fb4b19913981b8eda0468b0f26884d76 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Sun, 11 Feb 2018 22:00:18 +1000 Subject: [PATCH 02/12] Fix deserializing nullable structs by using the underlying type when nullable --- Octokit/Http/SimpleJsonSerializer.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/Octokit/Http/SimpleJsonSerializer.cs b/Octokit/Http/SimpleJsonSerializer.cs index e450d66c1f..5e7f115d22 100644 --- a/Octokit/Http/SimpleJsonSerializer.cs +++ b/Octokit/Http/SimpleJsonSerializer.cs @@ -144,6 +144,12 @@ public override object DeserializeObject(object value, Type type) if (stringValue != null) { + // If it's a nullable type, use the underlying type + if (ReflectionUtils.IsNullableType(type)) + { + type = Nullable.GetUnderlyingType(type); + } + var typeInfo = ReflectionUtils.GetTypeInfo(type); if (typeInfo.IsEnum) @@ -151,15 +157,6 @@ public override object DeserializeObject(object value, Type type) return DeserializeEnumHelper(stringValue, type); } - if (ReflectionUtils.IsNullableType(type)) - { - var underlyingType = Nullable.GetUnderlyingType(type); - if (ReflectionUtils.GetTypeInfo(underlyingType).IsEnum) - { - return DeserializeEnumHelper(stringValue, underlyingType); - } - } - if (ReflectionUtils.IsTypeGenericeCollectionInterface(type)) { // OAuth tokens might be a string of comma-separated values From ae3fcb29e81c04b077b221bfa5e1c877912d917d Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 07:54:00 +1000 Subject: [PATCH 03/12] StringEnum should behave like an enum, returning default(T) when it is uninitialised/null/blank --- Octokit.Tests/Models/StringEnumTests.cs | 12 ++++++++++++ Octokit.Tests/SimpleJsonSerializerTests.cs | 18 ++++++++++++++++-- Octokit/Models/Response/StringEnum.cs | 5 +++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Octokit.Tests/Models/StringEnumTests.cs b/Octokit.Tests/Models/StringEnumTests.cs index a19f435f9f..52e9a6931a 100644 --- a/Octokit.Tests/Models/StringEnumTests.cs +++ b/Octokit.Tests/Models/StringEnumTests.cs @@ -55,6 +55,18 @@ public void ShouldThrowForInvalidValue(string value) Assert.Throws(() => stringEnum.Value); } + public class SomeObject + { + public StringEnum SomeEnumProperty { get; set; } + } + + [Fact] + public void ShouldReturnDefaultWhenUninitialized() + { + var test = new SomeObject(); + Assert.Equal(AccountType.User, test.SomeEnumProperty.Value); + } + [Fact] public void ShouldHandleUnderscores() { diff --git a/Octokit.Tests/SimpleJsonSerializerTests.cs b/Octokit.Tests/SimpleJsonSerializerTests.cs index 37177128f2..7503d22873 100644 --- a/Octokit.Tests/SimpleJsonSerializerTests.cs +++ b/Octokit.Tests/SimpleJsonSerializerTests.cs @@ -116,12 +116,26 @@ public void HandlesEnum() var item = new ObjectWithEnumProperty { Name = "Ferris Bueller", - SomeEnum = SomeEnum.PlusOne + SomeEnum = SomeEnum.Unicode, + StringEnum = SomeEnum.SomethingElse }; var json = new SimpleJsonSerializer().Serialize(item); - Assert.Equal("{\"name\":\"Ferris Bueller\",\"some_enum\":\"+1\"}", json); + Assert.Equal("{\"name\":\"Ferris Bueller\",\"some_enum\":\"utf-8\",\"string_enum\":\"something_else\"}", json); + } + + [Fact] + public void HandlesEnumDefaults() + { + var item = new ObjectWithEnumProperty + { + Name = "Ferris Bueller" + }; + + var json = new SimpleJsonSerializer().Serialize(item); + + Assert.Equal("{\"name\":\"Ferris Bueller\",\"some_enum\":\"+1\",\"string_enum\":\"+1\"}", json); } } diff --git a/Octokit/Models/Response/StringEnum.cs b/Octokit/Models/Response/StringEnum.cs index 521fab7b5f..fc5d18fca6 100644 --- a/Octokit/Models/Response/StringEnum.cs +++ b/Octokit/Models/Response/StringEnum.cs @@ -144,6 +144,11 @@ public override string ToString() private TEnum ParseValue() { + if (string.IsNullOrEmpty(_stringValue)) + { + return default(TEnum); + } + TEnum value; if (TryParse(out value)) { From 6ba82d46a7668e28589102970c464bc36f43b120 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 07:54:53 +1000 Subject: [PATCH 04/12] Don't allow null to be passed into StringEnum ctor - if it needs to be null then it should be declared as nullable --- Octokit.Tests/Models/StringEnumTests.cs | 3 --- Octokit/Models/Response/StringEnum.cs | 8 +++++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/Octokit.Tests/Models/StringEnumTests.cs b/Octokit.Tests/Models/StringEnumTests.cs index 52e9a6931a..a65b657d32 100644 --- a/Octokit.Tests/Models/StringEnumTests.cs +++ b/Octokit.Tests/Models/StringEnumTests.cs @@ -46,12 +46,10 @@ public class TheParsedValueProperty { [Theory] [InlineData("")] - [InlineData(null)] [InlineData("Cow")] public void ShouldThrowForInvalidValue(string value) { var stringEnum = new StringEnum(value); - Assert.Throws(() => stringEnum.Value); } @@ -110,7 +108,6 @@ public void ShouldReturnTrueForValidValue() [Theory] [InlineData("")] - [InlineData(null)] [InlineData("Cow")] public void ShouldReturnFalseForInvalidValue(string value) { diff --git a/Octokit/Models/Response/StringEnum.cs b/Octokit/Models/Response/StringEnum.cs index fc5d18fca6..038a304ba3 100644 --- a/Octokit/Models/Response/StringEnum.cs +++ b/Octokit/Models/Response/StringEnum.cs @@ -29,7 +29,9 @@ public StringEnum(TEnum parsedValue) public StringEnum(string stringValue) { - _stringValue = stringValue ?? string.Empty; + Ensure.ArgumentNotNull(stringValue, nameof(stringValue)); + + _stringValue = stringValue; _parsedValue = null; } @@ -66,7 +68,7 @@ public bool TryParse(out TEnum value) return true; } - if (string.IsNullOrEmpty(StringValue)) + if (StringValue == null) { value = default(TEnum); return false; @@ -144,7 +146,7 @@ public override string ToString() private TEnum ParseValue() { - if (string.IsNullOrEmpty(_stringValue)) + if (_stringValue == null) { return default(TEnum); } From 2bfb3455a9910e41dd30e56ef713e1220d3968ec Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 21:53:12 +1000 Subject: [PATCH 05/12] fix expected json --- Octokit.Tests/SimpleJsonSerializerTests.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Octokit.Tests/SimpleJsonSerializerTests.cs b/Octokit.Tests/SimpleJsonSerializerTests.cs index 7503d22873..d3dfb36df3 100644 --- a/Octokit.Tests/SimpleJsonSerializerTests.cs +++ b/Octokit.Tests/SimpleJsonSerializerTests.cs @@ -117,12 +117,14 @@ public void HandlesEnum() { Name = "Ferris Bueller", SomeEnum = SomeEnum.Unicode, - StringEnum = SomeEnum.SomethingElse + SomeEnumNullable = SomeEnum.Unicode, + StringEnum = SomeEnum.SomethingElse, + StringEnumNullable = SomeEnum.SomethingElse }; var json = new SimpleJsonSerializer().Serialize(item); - Assert.Equal("{\"name\":\"Ferris Bueller\",\"some_enum\":\"utf-8\",\"string_enum\":\"something_else\"}", json); + Assert.Equal("{\"name\":\"Ferris Bueller\",\"some_enum\":\"unicode\",\"some_enum_nullable\":\"unicode\",\"string_enum\":\"something else\",\"string_enum_nullable\":\"something else\"}", json); } [Fact] From 8382cecf7cb0f02bc7696eda1084ab8106d765ab Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 21:54:18 +1000 Subject: [PATCH 06/12] move logic to determine if property is a StringEnum into helper function --- Octokit/Http/SimpleJsonSerializer.cs | 9 ++------- Octokit/SimpleJson.cs | 12 ++++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Octokit/Http/SimpleJsonSerializer.cs b/Octokit/Http/SimpleJsonSerializer.cs index 5e7f115d22..b74d9fe365 100644 --- a/Octokit/Http/SimpleJsonSerializer.cs +++ b/Octokit/Http/SimpleJsonSerializer.cs @@ -168,14 +168,9 @@ public override object DeserializeObject(object value, Type type) } } - if (typeInfo.IsGenericType) + if (ReflectionUtils.IsStringEnumWrapper(type)) { - var typeDefinition = typeInfo.GetGenericTypeDefinition(); - - if (typeof(StringEnum<>).IsAssignableFrom(typeDefinition)) - { - return Activator.CreateInstance(type, stringValue); - } + return Activator.CreateInstance(type, stringValue); } } else if (jsonValue != null) diff --git a/Octokit/SimpleJson.cs b/Octokit/SimpleJson.cs index 016e9aab4c..87653eb92e 100644 --- a/Octokit/SimpleJson.cs +++ b/Octokit/SimpleJson.cs @@ -1772,6 +1772,18 @@ public static bool IsAssignableFrom(Type type1, Type type2) return GetTypeInfo(type1).IsAssignableFrom(GetTypeInfo(type2)); } + public static bool IsStringEnumWrapper(Type type) + { + var typeInfo = ReflectionUtils.GetTypeInfo(type); + if (typeInfo.IsGenericType) + { + var typeDefinition = typeInfo.GetGenericTypeDefinition(); + + return typeof(StringEnum<>).IsAssignableFrom(typeDefinition); + } + return false; + } + public static IEnumerable GetInterfaces(Type type) { #if SIMPLE_JSON_TYPEINFO From 0b6e19ded7b67069898c826ff5224494e387685b Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 21:54:57 +1000 Subject: [PATCH 07/12] serializer needs to treat StringEnum specially by serializing the enum value according to existing serializer strategy (parameter attributes and so on) --- Octokit/Http/SimpleJsonSerializer.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Octokit/Http/SimpleJsonSerializer.cs b/Octokit/Http/SimpleJsonSerializer.cs index b74d9fe365..6755d69bb4 100644 --- a/Octokit/Http/SimpleJsonSerializer.cs +++ b/Octokit/Http/SimpleJsonSerializer.cs @@ -65,8 +65,21 @@ protected override bool TrySerializeUnknownTypes(object input, out object output Ensure.ArgumentNotNull(input, "input"); var type = input.GetType(); - var jsonObject = new JsonObject(); var getters = GetCache[type]; + + if (ReflectionUtils.IsStringEnumWrapper(type)) + { + // Handle StringEnum by getting the underlying enum value, then using the enum serializer + // Note this will throw if the StringEnum was initialised using a string that is not a valid enum member + var inputEnum = (getters["value"](input) as Enum); + if (inputEnum != null) + { + output = SerializeEnum(inputEnum); + return true; + } + } + + var jsonObject = new JsonObject(); foreach (var getter in getters) { if (getter.Value != null) From fb156d3ce00076e99a13d4a184fc95b3877333bb Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 22:29:03 +1000 Subject: [PATCH 08/12] remove fallback to default(T) --- Octokit/Models/Response/StringEnum.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/Octokit/Models/Response/StringEnum.cs b/Octokit/Models/Response/StringEnum.cs index 038a304ba3..6c46481de4 100644 --- a/Octokit/Models/Response/StringEnum.cs +++ b/Octokit/Models/Response/StringEnum.cs @@ -68,12 +68,6 @@ public bool TryParse(out TEnum value) return true; } - if (StringValue == null) - { - value = default(TEnum); - return false; - } - try { // Use the SimpleJsonSerializer to parse the string to Enum according to the GitHub Api strategy @@ -146,11 +140,6 @@ public override string ToString() private TEnum ParseValue() { - if (_stringValue == null) - { - return default(TEnum); - } - TEnum value; if (TryParse(out value)) { From 42d40ea4a4d6e87f911e2d49f33c35612430ae88 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 22:29:46 +1000 Subject: [PATCH 09/12] add test to assert ctor throws exception when null passed in --- Octokit.Tests/Models/StringEnumTests.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Octokit.Tests/Models/StringEnumTests.cs b/Octokit.Tests/Models/StringEnumTests.cs index a65b657d32..241ddffdf5 100644 --- a/Octokit.Tests/Models/StringEnumTests.cs +++ b/Octokit.Tests/Models/StringEnumTests.cs @@ -7,6 +7,12 @@ public class StringEnumTests { public class TheCtor { + [Fact] + public void ShouldThrowForNullStringValue() + { + Assert.Throws(() => new StringEnum(null)); + } + [Fact] public void ShouldSetValue() { From 1f868496ac963b0b09bb41f1730af3881273f9ad Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 22:30:17 +1000 Subject: [PATCH 10/12] remove test for default(T) fallback behaviour --- Octokit.Tests/Models/StringEnumTests.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/Octokit.Tests/Models/StringEnumTests.cs b/Octokit.Tests/Models/StringEnumTests.cs index 241ddffdf5..6626a53eac 100644 --- a/Octokit.Tests/Models/StringEnumTests.cs +++ b/Octokit.Tests/Models/StringEnumTests.cs @@ -48,7 +48,7 @@ public void ShouldThrowForInvalidEnumValue() } } - public class TheParsedValueProperty + public class TheValueProperty { [Theory] [InlineData("")] @@ -59,18 +59,6 @@ public void ShouldThrowForInvalidValue(string value) Assert.Throws(() => stringEnum.Value); } - public class SomeObject - { - public StringEnum SomeEnumProperty { get; set; } - } - - [Fact] - public void ShouldReturnDefaultWhenUninitialized() - { - var test = new SomeObject(); - Assert.Equal(AccountType.User, test.SomeEnumProperty.Value); - } - [Fact] public void ShouldHandleUnderscores() { From 959a34c25193803762895735556367fda9dcbae0 Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 22:30:58 +1000 Subject: [PATCH 11/12] Fix exception in serializer test - StringEnum property must be initialized otherwise an exception is thrown when attempting to serialize --- Octokit.Tests/SimpleJsonSerializerTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Octokit.Tests/SimpleJsonSerializerTests.cs b/Octokit.Tests/SimpleJsonSerializerTests.cs index d3dfb36df3..0d7be5098d 100644 --- a/Octokit.Tests/SimpleJsonSerializerTests.cs +++ b/Octokit.Tests/SimpleJsonSerializerTests.cs @@ -132,7 +132,9 @@ public void HandlesEnumDefaults() { var item = new ObjectWithEnumProperty { - Name = "Ferris Bueller" + Name = "Ferris Bueller", + SomeEnum = SomeEnum.PlusOne, + StringEnum = SomeEnum.PlusOne }; var json = new SimpleJsonSerializer().Serialize(item); From 60089fee6565674ed4043f6a0fd7697fb04814ca Mon Sep 17 00:00:00 2001 From: Ryan Gribble Date: Tue, 13 Feb 2018 23:01:16 +1000 Subject: [PATCH 12/12] Dont allow empty strings either --- Octokit.Tests/Models/StringEnumTests.cs | 20 +++++++++----------- Octokit/Models/Response/StringEnum.cs | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Octokit.Tests/Models/StringEnumTests.cs b/Octokit.Tests/Models/StringEnumTests.cs index 6626a53eac..69e0c72033 100644 --- a/Octokit.Tests/Models/StringEnumTests.cs +++ b/Octokit.Tests/Models/StringEnumTests.cs @@ -8,9 +8,11 @@ public class StringEnumTests public class TheCtor { [Fact] - public void ShouldThrowForNullStringValue() + public void ShouldThrowForNullOrEmptyStringValues() { Assert.Throws(() => new StringEnum(null)); + + Assert.Throws(() => new StringEnum("")); } [Fact] @@ -50,12 +52,10 @@ public void ShouldThrowForInvalidEnumValue() public class TheValueProperty { - [Theory] - [InlineData("")] - [InlineData("Cow")] - public void ShouldThrowForInvalidValue(string value) + [Fact] + public void ShouldThrowForInvalidValue() { - var stringEnum = new StringEnum(value); + var stringEnum = new StringEnum("Cow"); Assert.Throws(() => stringEnum.Value); } @@ -100,12 +100,10 @@ public void ShouldReturnTrueForValidValue() Assert.True(result); } - [Theory] - [InlineData("")] - [InlineData("Cow")] - public void ShouldReturnFalseForInvalidValue(string value) + [Fact] + public void ShouldReturnFalseForInvalidValue() { - var stringEnum = new StringEnum(value); + var stringEnum = new StringEnum("Cow"); AccountType type; var result = stringEnum.TryParse(out type); diff --git a/Octokit/Models/Response/StringEnum.cs b/Octokit/Models/Response/StringEnum.cs index 6c46481de4..86680c80a3 100644 --- a/Octokit/Models/Response/StringEnum.cs +++ b/Octokit/Models/Response/StringEnum.cs @@ -29,7 +29,7 @@ public StringEnum(TEnum parsedValue) public StringEnum(string stringValue) { - Ensure.ArgumentNotNull(stringValue, nameof(stringValue)); + Ensure.ArgumentNotNullOrEmptyString(stringValue, nameof(stringValue)); _stringValue = stringValue; _parsedValue = null;