Newtonsoft.Json icon indicating copy to clipboard operation
Newtonsoft.Json copied to clipboard

Android .net6 project throw exception when serilaize Tuple in Release mode (v13.0.2)

Open SPuV opened this issue 1 year ago • 5 comments

Steps

  1. Need create Android project on .net6
  2. Add reference to Newtonsoft.Json lib 13.0.2
  3. Create simple class with Tuple and try to serialize it.

Result: Debug mode works well. Release mode will throw exception.

Code simple

var tuple = new Tuple<decimal, string>(1, "one");
var json = JsonConvert.SerializeObject(tuple, Formatting.Indented);

Actual behavior

Code in release mode will trow exception after try serialize

Newtonsoft.Json.JsonSerializationException
Newtonsoft.Json.JsonSerializationException: A member with the name '' already exists on 'System.Tuple`2[System.Decimal,System.String]'. Use the JsonPropertyAttribute to specify another name.

Newtonsoft.Json.Serialization.JsonPropertyCollection.AddProperty(JsonProperty property)
Newtonsoft.Json.Serialization.DefaultContractResolver.CreateConstructorParameters(ConstructorInfo constructor, JsonPropertyCollection memberProperties)
Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract(Type objectType)
Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract(Type objectType)
System.Collections.Concurrent.ConcurrentDictionary`2[[System.Type, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Newtonsoft.Json.Serialization.JsonContract, Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed]].GetOrAdd(Type , Func`2 )
Newtonsoft.Json.Utilities.ThreadSafeStore`2[[System.Type, System.Private.CoreLib, Version=6.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e],[Newtonsoft.Json.Serialization.JsonContract, Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed]].Get(Type key)
Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveContract(Type type)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.WriteStartArray(JsonWriter writer, Object values, JsonArrayContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeList(JsonWriter writer, IEnumerable values, JsonArrayContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeObject(JsonWriter writer, Object value, JsonObjectContract contract, JsonProperty member, JsonContainerContract collectionContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.SerializeValue(JsonWriter writer, Object value, JsonContract valueContract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerProperty)
Newtonsoft.Json.Serialization.JsonSerializerInternalWriter.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
Newtonsoft.Json.JsonSerializer.SerializeInternal(JsonWriter jsonWriter, Object value, Type objectType)
Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object value, Type objectType)
Newtonsoft.Json.JsonConvert.SerializeObjectInternal(Object value, Type type, JsonSerializer jsonSerializer)
Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Type type, Formatting formatting, JsonSerializerSettings settings)
Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Formatting formatting, JsonSerializerSettings settings)

Expected behavior

Code will serialize in release mode without exception

SPuV avatar Jan 19 '23 16:01 SPuV

Are you by any chance trimming when building in Release mode?

elgonzo avatar Jan 19 '23 18:01 elgonzo

@elgonzo Yes, I think problem is in trimming. When I add <PublishTrimmed>false</PublishTrimmed> to project file, then application works fine in release mode, but size of our application is increased from 80 Mb to 190Mb. This way is not preferred.

SPuV avatar Jan 20 '23 10:01 SPuV

Then i would strongly recommend - if feasible - switching from Newtonsoft.Json to System.Text.Json, although i can't predict the success chance due to being oblivious of your project. Reflection-based serializers like Newtonsoft.Json are always a big problem regarding trimming (and native AOT compiling, for that matter), unfortunately.

elgonzo avatar Jan 20 '23 12:01 elgonzo

I don't think switching to System.Text.Json is the best solution. I prefer to use Newtonsoft.Json :)

At current moment I've fixed problem by adding System.Private.CoreLib lib to ignore list for trimming to .proj file

  <Target Name="ConfigureTrimming" BeforeTargets="PrepareForILLink">
    <ItemGroup>
      <ManagedAssemblyToLink Condition="'%(Filename)' == 'System.Private.CoreLib'">
        <IsTrimmable>false</IsTrimmable>
      </ManagedAssemblyToLink>
    </ItemGroup>
  </Target>

SPuV avatar Jan 20 '23 13:01 SPuV

Just my 5 cents, as a newish .net programmer, but lifelong programmer. I am having the same problem with a WASM client, but only when deployed to Azure Webapp.

When running locally there is no problem with retrieving a tuple that has an empty string as a parameter ( boolean, string="") - but it will throw exception only in release mode, eg. deployed as web app.

I guess my main contribution to this discussion is that it occurred in a WASM app, and not just android.

it also did not occur under .net 5 when we deployed to Azure Web App - only after migrating to .net 6 & .net 7

api Return -> Item1=true, Item2=""

failing code: var result = await _httpClient.GetFromJsonAsync<Tuple<bool, string>>(apiPath)

Result I then tried to use Newtonsoft, and System.Text.Json to deserialize json, using _httpClient.GetStringAsync(api_path),

but it always gave a problem with the empty string after publishing to azure web app. either with nullparameter, or nullname, or something like that. i can resetup environment if needed.

hope that helps

michaelhannes avatar May 24 '23 10:05 michaelhannes