• Categories
  • Recent
  • Tags
  • Popular
  • Solved
  • Unsolved
  • Users
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (Darkly)
  • No Skin
Collapse
brainCloud Forums

Newtonsoft.Json fails to deserialize integer fields after BrainCloud SDK upgrade 5.9.3

Scheduled Pinned Locked Moved Unsolved General
14 Posts 4 Posters 177 Views
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • L Offline
    L Offline
    LEE JONG GUN
    wrote last edited by
    #5

    Ok I will try Thank you!

    1 Reply Last reply
    0
  • Paul WinterhalderP Offline
    Paul WinterhalderP Offline
    Paul Winterhalder brainCloudAdmin
    wrote last edited by
    #6

    Hi @LEE-JONG-GUN - did you have any luck with that?

    1 Reply Last reply
    0
  • Paul WinterhalderP Offline
    Paul WinterhalderP Offline
    Paul Winterhalder brainCloudAdmin
    wrote last edited by
    #7

    Note - the client devs have asked me to point out that this serialization challenge is only for folks using LitJson. All the other libs seem to handle things just fine.

    L 2 Replies Last reply
    0
  • L Offline
    L Offline
    LEE JONG GUN
    replied to Paul Winterhalder last edited by
    #8

    @Paul-Winterhalder Thanks for the clarification.

    At the moment, I have not finished retesting everything yet
    because I am handling several other tasks in parallel.
    However, to give you an accurate answer, I am currently
    reinstalling and testing again on 5.9.3.

    For reference, this issue is not occurring with LitJson on
    our side. The deserialization error is happening with
    Newtonsoft.Json.JsonConvert.

    More importantly, the root problem is not just which JSON
    library is being used. The actual issue is that the raw
    response itself is already coming through differently.
    Before, this value came through as an integer, but in 5.9.3
    it is coming through as a double-form value such as 7.0.

    So from our perspective, the problem starts at the raw
    response level, before deserialization into our class.

    1 Reply Last reply
    0
  • L Offline
    L Offline
    LEE JONG GUN
    replied to Paul Winterhalder last edited by
    #9

    @Paul-Winterhalder 스크린샷 2026-03-14 오전 12.58.39.png

    The error we are seeing is:

    JsonReaderException: Input string '4.0' is not a valid integer. Path 'tier', line 2

    I tried the approach you suggested.

    I parsed the response with JObject first and then
    deserialized it again using JsonConvert.DeserializeObject.

    However, the error remains the same.

    JsonReaderException: Input string '4.0' is not a valid integer.

    It appears that the raw response value is already coming
    through as a double (e.g. 4.0). When Newtonsoft.Json tries
    to deserialize it into an int field, it fails.

    So simply passing the response through a
    serializer/deserializer step does not seem to resolve the
    issue in this case.

    Michael CostaM 1 Reply Last reply
    0
  • Michael CostaM Offline
    Michael CostaM Offline
    Michael Costa
    replied to LEE JONG GUN last edited by Michael Costa
    #10

    Hello @LEE-JONG-GUN!

    So looking into JsonConvert it looks like it actually uses LitJson underneath the hood 🤔

    So one option to replicate the old functionality is to use BrainCloud.JsonFx.Json.JsonReader to deserialize into Dictionary<string, object> and then BrainCloud.JsonFx.Json.JsonWriter back into a string before having JObject.Parse() use it. This is basically what the old behaviour was and it would strip out trailing .0 values from JS Numbers.

    SuccessCallback successCallback = (response, cbObject) =>
    {
        response = JsonWriter.Serialize(JsonReader.Deserialize(response)); // Replicate the old BCComms behaviour
    
        DisplayLog(string.Format("Success | {0}", response));
        JObject jsonData = JObject.Parse(response);
    
        if (jsonData["data"]["response"] != null && jsonData["status"].ToString() == "200")
        {
            scoreData = JsonConvert.DeserializeObject<ScoreData>(jsonData["data"]["response"].ToString()); // This should be working now as JsonWriter will strip trailing .0 from doubles
            // ...
        }
        // ...
    }
    

    Although you could also just use JsonReader instead of JsonConvert as this should also strip the trailing .0 so you don't have to waste CPU cycles on the serialization/deserialization:

    scoreData = JsonReader.Deserialize<ScoreData>(jsonData["data"]["response"].ToString());
    

    Alternatively you can also bypass using JObjects and JsonConvert in this situation and make use of our BrainCloud.Common.JsonParser together with JsonReader like so:

    SuccessCallback successCallback = (response, cbObject) =>
    {
        DisplayLog(string.Format("Success | {0}", response));
    
        if (JsonParser.GetString(response, "data", "response") is string responseData &&
            !string.IsNullOrEmpty(responseData) &&
            JsonParser.GetValue<int>(response, "status") == 200) // JsonParser can grab strings directly without having to do object memory allocations
        {
            scoreData = JsonReader.Deserialize<ScoreData>(responseData);
            // ...
        }
        // ...
    }
    

    I think JsonParser can be handy for grabbing strings of json objects, json arrays, and values directly without having to do memory allocations 😃

    Let us know if any of these solutions work for you!

    L 1 Reply Last reply
    0
  • L Offline
    L Offline
    LEE JONG GUN
    replied to Michael Costa last edited by
    #11

    @Michael-Costa

        string responseData = JsonParser.GetString(response, "data", "response");
            if (!string.IsNullOrEmpty(responseData) &&    JsonParser.GetValue<int>(response, "status") == 200){
    
                ScoreData3 scoreData = JsonReader.Deserialize<ScoreData3>(responseData);
                Debug.LogError(scoreData.stage_rank); >>WORKING
                Debug.LogError(scoreData.tier);>>WORKING
    

    We tested using the method you suggested, and with that approach
    the double values started coming through as integers correctly.

    However, another issue appeared because we are using Obscured
    Values in our project.

    When deserializing classes that contain Obscured types, those
    fields are not converted correctly with this method.

    The error we get is:

    InvalidCastException: Invalid cast from 'System.String'
    to 'CodeStage.AntiCheat.ObscuredTypes.ObscuredString'.

    If we switch back to using a JsonConverter, the original double
    problem happens again.

    Currently we resolve the Obscured type conversion using:

    JsonConvert.DefaultSettings = () =>
    new JsonSerializerSettings {
    Converters = { new ObscuredTypesNewtonsoftConverter() }
    };

    So my question is: does JsonReader provide a way to apply a
    converter globally in a similar way, like JsonConvert.DefaultSettings?

    Michael CostaM 1 Reply Last reply
    0
  • Michael CostaM Offline
    Michael CostaM Offline
    Michael Costa
    replied to LEE JONG GUN last edited by Michael Costa
    #12

    @LEE-JONG-GUN,

    Hmm, unfortunately I don't believe you can set JsonReader to be able to use a converter. In this case you might want to continue using JsonConvert then.

    The first solution I shared where you deserialize & serialize the response to strip out the trailing .0 might be your option here. Additionally, you can also modify your local copy of BrainCloudComms.cs to do this for all responses in-case this situation pops up elsewhere. This will essentially replicate the same behaviour in BC 5.9.2 and older.

    If that's the route you take you can modify BrainCloudComms.cs in the HandleResponseBundle function on line 972 like so:

    private void HandleResponseBundle(string jsonData)
    {
        jsonData = jsonData.Trim();
        if ((jsonData.StartsWith("{") && jsonData.EndsWith("}")) ||
            (jsonData.StartsWith("[") && jsonData.EndsWith("]")))
        {
            jsonData = JsonWriter.Serialize(JsonReader.Deserialize(jsonData)); // This will strip any leading .0 for int values, replicating the behaviour from 5.9.2
        }
    

    We will have to look into if we should have this be an optional flag that can be enabled for compatibility... Do note though that this will mean that a lot of the memory and performance gains from 5.9.3 will be lost.

    There is another option though and that's to leverage JsonConverter 😃

    Here's a script that should take floats/doubles during deserialization and convert them to an int. I modified it and tested it, I think it should work for this situation:

    using System;
    using Newtonsoft.Json;
    
    /// <summary>
    /// Converts JSON floating-point numbers (float/double) to int during deserialization.
    /// </summary>
    public class FloatToIntJsonConverter : JsonConverter<int>
    {
        public override int ReadJson(JsonReader reader, Type objectType, int existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            switch (reader.TokenType)
            {
                case JsonToken.Float:
                    return Convert.ToInt32(Math.Round(Convert.ToDouble(reader.Value), MidpointRounding.AwayFromZero));
    
                case JsonToken.Integer:
                    return Convert.ToInt32(reader.Value);
    
                case JsonToken.String:
                    string sVal = reader.Value?.ToString() ?? "0";
                    if (double.TryParse(sVal, out double parsed))
                    {
                        return Convert.ToInt32(Math.Round(parsed, MidpointRounding.AwayFromZero));
                    }
    
                    throw new JsonSerializationException($"Cannot deserialize string to int: {sVal}");
    
                case JsonToken.Null:
                    return 0;
    
                default:
                    throw new JsonSerializationException($"Unexpected token when tryign to deserialize as int: {reader.TokenType}");
            }
        }
    
        public override void WriteJson(JsonWriter writer, int value, JsonSerializer serializer)
        {
            writer.WriteValue(value);
        }
    }
    

    You can apply this to any specific problematic int like so:

    public class ScoreData
    {
        [JsonConverter(typeof(FloatToIntJsonConverter))]
        public int tier;
    }
    

    Of course you can also apply it globally, but that might be a bit riskier to do 😅 Also note that this script will round floats/doubles to the nearest whole number so if you end up using it make sure to keep that in-mind (or feel free to modify it to suit your needs).

    Let us know if either of these work out for you!

    L 1 Reply Last reply
    0
  • L Offline
    L Offline
    LEE JONG GUN
    replied to Michael Costa last edited by
    #13

    @Michael-Costa
    The second option seems too narrow in scope for us, since it would
    require us to apply it individually to each affected field, which
    is not very practical in our case.

    With the first approach, the raw response is now being normalized
    back to the same format we were seeing in 5.9.2, so we will proceed
    with that as our workaround for now.

    Thank you

    1 Reply Last reply
    0
  • L Offline
    L Offline
    LEE JONG GUN
    wrote last edited by
    #14

    Symptom
    After updating from 5.9.2 to 5.9.3, iOS/iPadOS experiences gradual performance degradation during extended play sessions, eventually leading to app termination
    This issue did not occur on 5.9.2
    Suspected Code
    In BrainCloudComms.cs - HandleResponseBundle, the following line was added in 5.9.3:

    jsonData = JsonWriter.Serialize(JsonReader.Deserialize(jsonData));
    Concerns
    This performs a full deserialize → reserialize on every API response
    Immediately after, DeserializeJsonBundle parses the same data again — effectively parsing every response twice
    Each response generates a large number of temporary objects (Dictionary, List, boxed values, strings) that are immediately discarded
    On iOS with IL2CPP (Boehm GC), could this repeated allocation/deallocation pattern cause managed heap growth over time?
    Question
    Could this code potentially contribute to memory-related issues on iOS during extended play sessions?

    1 Reply Last reply
    0

  • Login

  • Login or register to search.
  • First post
    Last post
0
  • Categories
  • Recent
  • Tags
  • Popular
  • Solved
  • Unsolved
  • Users