Search code examples
c#jsonlocalizationdeserialization

Modify json reader with no access to editing the reader class


We are working on a localization mod for Il2cpp game. Initially, to apply localization to the game, we were launching this code that has all properties and lines from source JSON file. We have private Dictionary<string, GameClass> __instance._dic that is written in the game. The question is... Is there a way to modify the JSON value class without touching the said class in the game code? I want to add some of my own lines to the JSON file and make them readable in this class.

Me and my friend have tried to extend the class by making new ones: CustomClassList and CustomClass. I'm completely lost at this point.

var jsonString = File.ReadAllText("Myfile.json");
GameClassList Localization = JsonSerializer.Deserialize<GameClassList>(jsonString);
foreach (var applyChange in Localization.assets)
{
    __instance._dic[applyChange.name] = applyChange;
}

(The names of classes are placeholders)

The game either doesn't see the CustomClass and CustomClassList or, when the code is modified, throws an error in the game that CustomClass cannot be converted to GameClass. There's no compiling errors on my side, and conversion errors on the side of my friend.

EDIT: Since I am the only one left to work with this problem in the team, I guess I can show more code for better understanding. I also give up on placeholding the names of our custom classes, too.

That's the custom child class for adding new fields and making a List<> with them.

{
    [Serializable]
    public class NicknameData : *GameClass*
    {
        public string? runame;
        public string? ruNickName;

        public NicknameData() {}
        public static *GameClass* HELP(NicknameData data)
        {
            var PLEASE = JsonSerializer.Serialize(data);
            *GAMECLASS* assetData = JsonSerializer.Deserialize<*GameClass*>(PLEASE);
            return assetData;
        }
    }
    [Serializable]
    public class NicknameDataList : *GameClassList*
    {
        public new Il2CppSystem.Collections.Generic.List<NicknameData> assetData;

        public NicknameDataList() { }
    }
}

And that's the Harmony code my friend did (and I have modified) before the version above:

public static string nicknameJSON = File.ReadAllText(*JSON_FILE.json*);
public static NicknameDataList nicknameDataList = JsonSerializer.Deserialize<NicknameDataList>(nicknameJSON);

[HarmonyPatch(typeof(StoryData), nameof(StoryData.Init))]
[HarmonyPostfix]
private static void StoryDataInit(StoryData __instance)
{
    *GameClassList* blyahaMuha = nicknameDataList;
    
    foreach (var nicknameData in blyahaMuha.assetData)
    {
        __instance.*_dic*[nicknameData.name] = (*GameClass*)NicknameData.HELP((NicknameData)nicknameData);
    }
}

Since this code has pieces of game's code, I have changed some names to placeholders to not get in trouble. The changed values are surrounded with "**".

I hope this edit makes the question and the problem more understandable.


Solution

  • In the end, with the help of some acquaintances I've looked at the situation from a different view. Instead of trying to make NicknameData act as its parent, we changed the way the names and titles in the game are called.

    Even though now the game deserializes the said JSON twice, it's pretty safe working with the times when the character array lacks the needed lines.

    Now the Nickname Data looks like this:

        [Serializable]
    public class NicknameData : *GameClass*
    {
        [JsonPropertyName("runame")]
        public string? runame;
    
        [JsonPropertyName("ruNickName")]
        public string? ruNickName;
    
        public NicknameData() { }
    
        internal static NicknameData Create(ref Utf8JsonReader reader)
        {
            var result = new NicknameData();
            while (reader.Read())
            {
                if (reader.TokenType == JsonTokenType.EndObject)
                {
                    break;
                }
                 if (reader.TokenType == JsonTokenType.PropertyName)
                {
                    string propertyName = reader.GetString();
                    reader.Read();
    
                    if (propertyName == "name")
                    {
                        result.name = reader.GetString();
                    }
                    else if (propertyName == "runame")
                    {
                        result.runame = reader.GetString();
                    }
                    else if (propertyName == "ruNickName")
                    {
                        result.ruNickName = reader.GetString();
                    }
                }
            }
            return result;
        }
    }
    

    Beside that, I was given the custom converter which is, unlike built-in ones, working without sending errors of inability to converse into parent class or, which is more frequent, the Exception: System.NullReferenceException: Object reference not set to an instance of an object. error.

    It's quite long so I'll edit the link of the converter into the answer once we push the code update onto our localization GitHub.

    Either way, thank you, those who tried to help me! Now the localization idea which haunted me for weeks has come to fruition and I feel really happy and relieved.