Как сериализовать и десериализовать json и json массив в Unity3d?

769
28 апреля 2017, 16:43

У меня имеется список элементов, которые присылаются c серверной стороны (присланные, к примеру, от PHP) используя [WWW(https://docs.unity3d.com/ScriptReference/WWW.html). Его контент (WWW.text) выглядит так:

[{"playerId":"1","playerLoc":"Powai"},{"playerId":"2","playerLoc":"Andheri"},{"playerId":"3","playerLoc":"Churchgate"}] 

Класс, который я использую выглядит так:

public class player {
    public string playerId { get; set; }
    public string playerLoc { get; set; }
    public string playerNick { get; set; }
}

Пробовал десериализовать строку разными способами:

  1. Использовал Boomlagoon.JSON, но он почему-то извлекает только первый объект.
  2. Пытался использовать MiniJSON: обрезал [] у присланной строки и затем применял библиотеку:

    IDictionary<string,object> s = Json.Deserialize(serviceData) as IDictionary<string,object>;
    foreach (KeyValuePair<string, object> kvp in s) {
        Debug.Log(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
    }
    

    Но оно возвращает первое значение `KeyValuePair:

    Key = playerId, Value = 1
    Key = playerLoc, Value = Powai
    

    А если оставить скобки, то вообще ничего не может распарсить json и поэтому логично в foreach кидает ошибку:

    NullReferenceException: Object reference not set to an instance of an object

Как мне корректно сериализовать и десериализовать данные из json? Что использовать и как?

Перевод вопроса http://stackoverflow.com/q/36239705/6104996

Answer 1

Начиная с версии 5.3.3 Unity добавила JsonUtility в своё API. Можете забыть о различных сторонних библиотеках, разве что вы не делаете что-то очень сложное.

JsonUtility быстрее, чем эти сторонние библиотеки, но и, меж тем, поддерживаются только просты типы. Оно не поддерживает коллекции, такие как словарь (Dictionary). Исключением является список (List). Оно поддерживает списки (List) и массив списков (List array).

Если вам нужно сериализовать словарь (Dictionary) или сделать что-то, чем простая сериализация и десеризация простых типов — используйте сторонние библиотеки. В ином же случае обновитесь до версии >= 5.3.3 и попробуйте решения, которые приведены ниже.

Пример класса для сериализации:

[Serializable]
public class Player {
    public string playerId;
    public string playerLoc;
    public string playerNick;
}

1. ОДИН ОБЪЕКТ (НЕ JSON МАССИВ)

Исходные данные:

Player playerInstance = new Player();
playerInstance.playerId = "8484239823";
playerInstance.playerLoc = "Powai";
playerInstance.playerNick = "Random Nick";

Сериализация 1:

Сериализация в json с помощью метода public static string ToJson(object obj);.

string playerToJson = JsonUtility.ToJson(playerInstance);
Debug.Log(playerToJson);

Output:

{"playerId":"8484239823","playerLoc":"Powai","playerNick":"Random Nick"}

Сериализация 2:

Сериализация в json с помощью перегруженного метода public static string ToJson(object obj, bool prettyPrint);. Добавив флаг true вторым аргументом в функцию JsonUtility.ToJson отформатирует данные. Сравните вывод ниже с выводом, что был выше.

string playerToJson = JsonUtility.ToJson(playerInstance, true);
Debug.Log(playerToJson);

Output:

{
    "playerId": "8484239823",
    "playerLoc": "Powai",
    "playerNick": "Random Nick"
}

Десериализация 1:

Десериализация json c помощью метода public static T FromJson(string json);.

string jsonString = "{\"playerId\":\"8484239823\",\"playerLoc\":\"Powai\",\"playerNick\":\"Random Nick\"}";
Player player = JsonUtility.FromJson<Player>(jsonString);
Debug.Log(player.playerLoc);

Десериализация 2:

Десериализация json c помощью перегруженного метода public static object FromJson(string json, Type type);.

string jsonString = "{\"playerId\":\"8484239823\",\"playerLoc\":\"Powai\",\"playerNick\":\"Random Nick\"}";
Player player = (Player)JsonUtility.FromJson(jsonString, typeof(Player));
Debug.Log(player.playerLoc);

Десериализация 3:

Десериализация json c помощью метода public static void FromJsonOverwrite(string json, object objectToOverwrite);.

Когда используется метод JsonUtility.FromJsonOverwrite, то новый экземпляр объекта не будет создан, а будет переиспользован объект, который вы передадите в качестве параметра в функцию, перезаписав его значения.

Player playerInstance;
void Start() {
    // создаем экземпляр объекта
    playerInstance = new Player();
    deserialize();
}
void deserialize() {
    string jsonString = "{\"playerId\":\"8484239823\",\"playerLoc\":\"Powai\",\"playerNick\":\"Random Nick\"}";    
    // Перезаписываем значения уже существующего экземпляра класса "playerInstance". Меньше затрат памяти.
    JsonUtility.FromJsonOverwrite(jsonString, playerInstance);
    Debug.Log(playerInstance.playerLoc);
}

2. МНОЖЕСТВО ДАННЫХ (массив json): ваш json содержит несколько объектов с данными.

JsonUtility пока в нативном виде не поддерживает массивы, но можно воспользоваться небольшим классом-хэлпером, чтобы получить массив, который будет работать с JsonUtility.

Создаем класс JsonHelper. Можете скопировать код хэлпера, представленного ниже:

public static class JsonHelper {
    public static T[] FromJson<T>(string json) {
        Wrapper<T> wrapper = JsonUtility.FromJson<Wrapper<T>>(json);
        return wrapper.Items;
    }
    public static string ToJson<T>(T[] array) {
        Wrapper<T> wrapper = new Wrapper<T>();
        wrapper.Items = array;
        return JsonUtility.ToJson(wrapper);
    }
    public static string ToJson<T>(T[] array, bool prettyPrint) {
        Wrapper<T> wrapper = new Wrapper<T>();
        wrapper.Items = array;
        return JsonUtility.ToJson(wrapper, prettyPrint);
    }
    [Serializable]
    private class Wrapper<T> {
        public T[] Items;
    }
}

Сериализация json массива:

Player[] playerInstance = new Player[2];
playerInstance[0] = new Player();
playerInstance[0].playerId = "8484239823"; 
playerInstance[0].playerLoc = "Powai"; 
playerInstance[0].playerNick = "Random Nick";
playerInstance[1] = new Player();
playerInstance[1].playerId = "512343283"; 
playerInstance[1].playerLoc = "User2"; 
playerInstance[1].playerNick = "Rand Nick 2";
// Конвертируем в json
string playerToJson = JsonHelper.ToJson(playerInstance, true);
Debug.Log(playerToJson);

Output:

{
    "Items": [
        {
            "playerId": "8484239823",
            "playerLoc": "Powai",
            "playerNick": "Random Nick"
        },
        {
            "playerId": "512343283",
            "playerLoc": "User2",
            "playerNick": "Rand Nick 2"
        }
    ]
}

Десериализация json массива:

string jsonString = "{\r\n    \"Items\": [\r\n        {\r\n            \"playerId\": \"8484239823\",\r\n            \"playerLoc\": \"Powai\",\r\n            \"playerNick\": \"Random Nick\"\r\n        },\r\n        {\r\n            \"playerId\": \"512343283\",\r\n            \"playerLoc\": \"User2\",\r\n            \"playerNick\": \"Rand Nick 2\"\r\n        }\r\n    ]\r\n}";
Player[] player = JsonHelper.FromJson<Player>(jsonString);
Debug.Log(player[0].playerLoc);
Debug.Log(player[1].playerLoc);

Output:

Powai

User2

Примечание: если данный json массив приходит с сервера и вы не создаете его самолично вручную, то тогда надо добавить {"Items": в начало полученной строки и } — в конец данной строки.

Вот простая функция, чтобы сделать это:

string fixJson(string value) {
    value = "{\"Items\":" + value + "}";
    return value;
}

Затем использовать её:

string jsonString = fixJson(yourJsonFromServer);
Player[] player = JsonHelper.FromJson<Player>(jsonString);

3. ИСПРАВЛЕНИЕ ПРОБЛЕМ JsonUtility:

Возникают проблемы при сериализации при использовании JsonUtility.ToJson? Получаете пустую строку или "{}"?

  • Убедитесь, что класс не является массивом. Если это не так, то используйте класс-хэлпер выше и его метод JsonHelper.ToJson, вместо JsonUtility.ToJson.
  • Не забудьте добавить атрибут [Serializable] сверху над объявлением класса, который вы хотите сериализовать.
  • Удалите свойства из класса. Например, в переменной public string playerId { get; set; } удалите { get; set; }. Unity не сможет сериализовать их.

Проблемы при десериализации при использовании JsonUtility.FromJson?

  • Если вы получаете Null, то убедитесь, что json не является json массивом. Если это так, то используйте класс-хэлпер выше и его метод JsonHelper.FromJson вместо JsonUtility.FromJson.
  • Если вы получаете NullReferenceException в процессе десериализации, то добавьте атрибут [Serializable] сверху над объявлением класса.

Отвечая, на ваш вопрос:

Ваши первоначальные данные:

 [{"playerId":"1","playerLoc":"Powai"},{"playerId":"2","playerLoc":"Andheri"},{"playerId":"3","playerLoc":"Churchgate"}]

Добавьте {"Items": в начало этой строки и добавьте }в конец этой строки.

Код выглядит так:

serviceData = "{\"Items\":" + serviceData + "}";

Теперь у вас есть строка:

 {"Items":[{"playerId":"1","playerLoc":"Powai"},{"playerId":"2","playerLoc":"Andheri"},{"playerId":"3","playerLoc":"Churchgate"}]}

Чтобы сериализовать теперь это множество данных из php как массивы, вы можете сделать так:

public player[] playerInstance;
playerInstance = JsonHelper.FromJson<player>(serviceData);
  • playerInstance[0] — данные из первого объекта
  • playerInstance[1] — данные из второго объекта
  • playerInstance[2] — данные из третьего объекта

Примечание: Удалите { get; set; } из класса player. Если у вас имеются { get; set; }, то работать не будет. JsonUtility НЕ работает с членами класса, которые объявлены как свойства.

Если они нужны, тогда придется использовать либо другие библиотеки, либо XmlSerializer/BinaryFormatter

Перевод ответа http://stackoverflow.com/a/36244111/6104996

READ ALSO
Как получить информацию о месте клика в WebBrowser

Как получить информацию о месте клика в WebBrowser

Подскажите, пожалуйста, как можно получить информацию о месте клика при работе с WebBrowser

266
Проверка существования значения в БД MS SQL

Проверка существования значения в БД MS SQL

Есть БД MS SQL, в ней есть записи содержащие информацию о человеке (ФИО, возраст, город)Хочу сделать чтобы при добавлении новой записи была выполнена...

511
C#(xNet) загрузка данных из файла .txt

C#(xNet) загрузка данных из файла .txt

Здравствуйте, подскажите пожалуйста как можно реализовать загрузку файла(например изtxt вида логин/пароль) на языке с# при помощи библиотеки...

359
Аутентификация при использовании WebRequest и WebResponse

Аутентификация при использовании WebRequest и WebResponse

Нужно авторизоваться на сайте по протоколу https - при помощи WebRequest и WebResponse (сама программа на ASPNET MVC)

295