launchdarkly / dotnet-client-sdk

LaunchDarkly Client-side SDK for .NET
Other
7 stars 10 forks source link

JSON Exception in InitAsync #22

Closed drjaydenm closed 4 years ago

drjaydenm commented 4 years ago

Describe the bug When calling InitAsync we are getting a JSON serialization error A member with the name 'anonymous' already exists on 'LaunchDarkly.Client.User'. Use the JsonPropertyAttribute to specify another name.

To reproduce

var user = User.Builder("123").Build();
var ldClient = await LdClient.InitAsync("KEY HERE", user);

Expected behavior Initialization completes without an exception :)

Logs Stack trace

{Newtonsoft.Json.JsonSerializationException: A member with the name 'anonymous' already exists on 'LaunchDarkly.Client.User'. Use the JsonPropertyAttribute to specify another name. at Newtonsoft.Json.Serialization.JsonPropertyCollection.AddProperty (Newtonsoft.Json.Serialization.JsonProperty property) [0x0011d] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateProperties (System.Type type, Newtonsoft.Json.MemberSerialization memberSerialization) [0x00072] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract (System.Type objectType) [0x0003a] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract (System.Type objectType) [0x0010f] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.ResolveContract (System.Type type) [0x00036] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.GetContractSafe (System.Object value) [0x00016] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) [0x00028] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.JsonSerializer.SerializeInternal (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) [0x0023a] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.JsonSerializer.Serialize (Newtonsoft.Json.JsonWriter jsonWriter, System.Object value, System.Type objectType) [0x00000] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.JsonConvert.SerializeObjectInternal (System.Object value, System.Type type, Newtonsoft.Json.JsonSerializer jsonSerializer) [0x00028] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.JsonConvert.SerializeObject (System.Object value, System.Type type, Newtonsoft.Json.JsonSerializerSettings settings) [0x00007] in <12891e825fce44a581e5bbbb579c1d49>:0 at Newtonsoft.Json.JsonConvert.SerializeObject (System.Object value, Newtonsoft.Json.JsonSerializerSettings settings) [0x00000] in <12891e825fce44a581e5bbbb579c1d49>:0 at LaunchDarkly.Xamarin.JsonUtil.EncodeJson (System.Object o) [0x00001] in :0 at LaunchDarkly.Xamarin.Extensions.AsJson (LaunchDarkly.Client.User user) [0x00001] in :0 at LaunchDarkly.Xamarin.MobileStreamingProcessor.MakeStreamPropertiesForGet () [0x00001] in :0 at LaunchDarkly.Xamarin.MobileStreamingProcessor..ctor (LaunchDarkly.Xamarin.Configuration configuration, LaunchDarkly.Xamarin.IFlagCacheManager cacheManager, LaunchDarkly.Client.User user, LaunchDarkly.Common.StreamManager+EventSourceCreator eventSourceCreator) [0x0002a] in :0 at LaunchDarkly.Xamarin.Factory+<>c__DisplayClass3_0.b0 () [0x0005a] in :0 at LaunchDarkly.Xamarin.ConnectionManager.OpenOrCloseConnectionIfNecessary () [0x0004d] in :0 at LaunchDarkly.Xamarin.ConnectionManager.b18_0 () [0x00021] in :0 at LaunchDarkly.Xamarin.LockUtils.WithWriteLock[T] (System.Threading.ReaderWriterLockSlim rwLock, System.Func`1[TResult] fn) [0x00009] in :0 at LaunchDarkly.Xamarin.ConnectionManager.Start () [0x00001] in :0 at LaunchDarkly.Xamarin.LdClient.StartAsync () [0x0000f] in :0 at LaunchDarkly.Xamarin.LdClient.InitAsync (LaunchDarkly.Xamarin.Configuration config, LaunchDarkly.Client.User user) [0x00080] in :0 …

SDK version 1.1.0

Language version, developer tools Xamarin.Forms 4.2.0.848062 Newtonsoft.Json 12.0.2

OS/platform iOS 13.1 Android 9.0

drjaydenm commented 4 years ago

Looking into it more, it looks to be caused by the latest 4.* changes in the CommonSdk here.

If I add a hard dependency on LaunchDarkly.CommonSdk 4.0.1 it works correctly. This issue might actually be better placed on the shared library github, feel free to move it around

eli-darkly commented 4 years ago
  1. Please don't mix and match versions of XamarinSdk and CommonSdk— the former relies on features in the specific version of the latter that it was built with.

  2. You're probably right that this is a CommonSdk bug, but it still makes sense to track it here since this symptom is not reproducible in the .NET SDK.

  3. I'm having trouble seeing why we would not have been able to reproduce this symptom in our own automated and manual testing. It seems like it'd be a pretty straightforward problem. My best guess so far is that we may have been testing with a different version of Newtonsoft.Json.

eli-darkly commented 4 years ago

Well, the behavior almost makes sense to me:

  1. Newtonsoft.Json by default will serialize all properties that don't have [JsonIgnore]. We neglected to add that attribute to the Anonymous property when we changed it to serialize AnonymousOptional with the name "anonymous". So it makes sense that it is trying to serialize both of them. That would be a straightforward fix.
  2. However, by default it does not change the casing of the property name. So I would expect it to end up serializing both "anonymous" and "Anonymous", which is not a conflict since JSON property names are case-sensitive. And in fact that is exactly what it does in all of my tests, both for Newtonsoft.JSON 9.0.1 (which we build against) and 12.0.2 (which you're using).

So, unfortunately I can't reproduce the behavior you're seeing and can't see why it would fail in this way, but it is still somewhat wrong in any case, so there's a fix we can make that may fix what you're seeing.

drjaydenm commented 4 years ago

Thanks for getting back so quickly. Now that you mention the auto-lowercasing not being the default behaviour, I wouldn’t be surprised if we have this happening automatically in our newtonsoft setup. I’ll double check to confirm and get back to you.

eli-darkly commented 4 years ago

Ah, that might explain it— I didn't realize it was possible to customize that on a global level. In any case, we should still fix the extra property encoding.

drjaydenm commented 4 years ago

Yep looks like we are indeed using a global serializer contract which is set to camel case. This must be impacting the LaunchDarkly API payload causing the two anonymous properties to clash :)

JsonConvert.DefaultSettings = () => new JsonSerializerSettings
{
    ContractResolver = new CamelCasePropertyNamesContractResolver()
};

Camel case contract documentation

eli-darkly commented 4 years ago

There's a fix for this in the 1.1.1 release. Please let us know when you've had a chance to retest with this version.

drjaydenm commented 4 years ago

Sorry for the delayed response, but we've finally had a chance to test out this fix.

Everything looks to be working as expected now on the latest version, thanks for all your efforts

eli-darkly commented 4 years ago

@drjaydenm Glad to hear it!