Серіалізувати та десериалізувати Json та Json Array в Unity


97

У мене є список елементів, які надсилаються з файлу PHP до цілісності WWW.

У WWW.textвиглядає наступним чином :

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

Де я обрізаю зайве []з string. Коли я намагаюся проаналізувати його за допомогою Boomlagoon.JSON, отримується лише перший об’єкт. Я з’ясував, що маю deserialize()список і імпортував MiniJSON.

Але мене бентежить, як скласти deserialize()цей список. Я хочу прокрутити кожен об'єкт JSON і отримати дані. Як я можу це зробити в Unity за допомогою C #?

Клас, яким я користуюся, є

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

Після обрізки []я можу проаналізувати json за допомогою MiniJSON. Але повертається лише перше KeyValuePair.

IDictionary<string, object> players = Json.Deserialize(serviceData) as IDictionary<string, object>;

foreach (KeyValuePair<string, object> kvp in players)
{
    Debug.Log(string.Format("Key = {0}, Value = {1}", kvp.Key, kvp.Value));
}

Дякую!


Чому ви прибрали зовнішній [і ]? Ось що робить його списком. Просто припиніть видаляти це та десеріалізуйте його як масив або список, і я сподівався б, що це буде добре. Будь ласка, опублікуйте код, який ви спробували.
Джон Скіт,

Покажіть клас, який використовується для десериалізації. Формат дивний, чому другий програвач не загорнутий у фігурні дужки? Він повинен десеріалізациі в список чого - то, як List<PlayerLocation>, тому що це є масивом.
Максиміліан Герхардт

@MaximilianGerhardt Вибачте, фігурні дужки були друкарською помилкою. Виправлено у питанні та додано також код. Дякую.
dil33pm

1
Я думаю, що щось не так із вашим розумінням того, як ця бібліотека тут обробляє десеріалізацію. Це не звичайна десериалізація (як ви, можливо, бачите Newtonsoft.Json), але Json.Deserialize()ЗАВЖДИ повертається IDictionary<string,object>до вас, і ви оперуєте List<object>. Подивіться на stackoverflow.com/a/22745634/5296568 . Бажано отримати кращий десериалізатор JSON, який виконує десериалізацію, до якої ви звикли.
Максиміліан Герхардт

@MaximilianGerhardt Я намагався IDictionary<string,object>. Я можу отримати значення, але лише перше KeyValuePair<>.
dil33pm

Відповіді:


247

Unity додав JsonUtility до свого API після оновлення 5.3.3 . Забудьте про всі сторонні бібліотеки, якщо ви не робите щось складніше. JsonUtility працює швидше, ніж інші бібліотеки Json. Оновіть версію Unity 5.3.3 або новішу, а потім спробуйте наведене нижче рішення.

JsonUtility- це легкий API. Підтримуються лише прості типи. Він не підтримує колекції, такі як Словник. Одним винятком є List. Він підтримує Listі Listмасив!

Якщо вам потрібно серіалізувати Dictionaryабо зробити щось інше, ніж просто серіалізувати та десеріалізувати прості типи даних, використовуйте сторонні API. В іншому випадку продовжуйте читати.

Приклад класу для серіалізації:

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

1. ОДИН ОБ'ЄКТ ДАНИХ (НЕМАТИЧНИЙ JSON)

Серіалізація частини А :

Серіалізуйте Json public static string ToJson(object obj);методом.

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

//Convert to JSON
string playerToJson = JsonUtility.ToJson(playerInstance);
Debug.Log(playerToJson);

Вихід :

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

Серіалізація частини B :

Серіалізувати до Json із public static string ToJson(object obj, bool prettyPrint);перевантаженням методу. Просто перехід trueдо JsonUtility.ToJsonфункції відформатує дані. Порівняйте вихідні дані з вихідними.

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

//Convert to JSON
string playerToJson = JsonUtility.ToJson(playerInstance, true);
Debug.Log(playerToJson);

Вихід :

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

Десериалізація частини А :

Десеріалізуйте json 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);

Десериалізація частини В :

Десеріалізуйте json 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);

Десериалізація частини C :

Десериалізуйте json за допомогою public static void FromJsonOverwrite(string json, object objectToOverwrite);методу. При JsonUtility.FromJsonOverwriteвикористанні жоден новий екземпляр цього Об’єкта, до якого ви десериалізуєтесь, створюватися не буде. Він просто повторно використає екземпляр, який ви передасте, і перезапише його значення.

Це ефективно, і його слід використовувати, якщо це можливо.

Player playerInstance;
void Start()
{
    //Must create instance once
    playerInstance = new Player();
    deserialize();
}

void deserialize()
{
    string jsonString = "{\"playerId\":\"8484239823\",\"playerLoc\":\"Powai\",\"playerNick\":\"Random Nick\"}";

    //Overwrite the values in the existing class instance "playerInstance". Less memory Allocation
    JsonUtility.FromJsonOverwrite(jsonString, playerInstance);
    Debug.Log(playerInstance.playerLoc);
}

2. БАГАТО ДАНИХ (ARRAY JSON)

Ваш Json містить кілька об'єктів даних. Наприклад, playerIdз'являвся не раз . Юніті JsonUtilityне підтримує масив , як це все ще новий , але ви можете використовувати допоміжний клас від цієї людини , щоб отримати масив роботи з JsonUtility.

Створіть клас з назвою JsonHelper. Скопіюйте 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";

//Convert to JSON
string playerToJson = JsonHelper.ToJson(playerInstance, true);
Debug.Log(playerToJson);

Вихід :

{
    "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);

Вихід :

Повай

Користувач2


Якщо це масив Json із сервера, і ви не створили його вручну :

Можливо, вам доведеться додати {"Items":перед отриманим рядком, а потім додати }в кінці.

Я зробив для цього просту функцію:

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

тоді ви можете використовувати його:

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

3. Десериалізуйте рядок json без класу && Десериализуйте Json з числовими властивостями

Це Json, який починається з числа або числових властивостей.

Наприклад:

{ 
"USD" : {"15m" : 1740.01, "last" : 1740.01, "buy" : 1740.01, "sell" : 1744.74, "symbol" : "$"}, 

"ISK" : {"15m" : 179479.11, "last" : 179479.11, "buy" : 179479.11, "sell" : 179967, "symbol" : "kr"},

"NZD" : {"15m" : 2522.84, "last" : 2522.84, "buy" : 2522.84, "sell" : 2529.69, "symbol" : "$"}
}

Unity's JsonUtilityцього не підтримує, оскільки властивість "15 м" починається з числа. Змінна класу не може починатися з цілого числа.

Завантажте SimpleJSON.csз вікі Unity .

Щоб отримати майно "15 млн. Доларів США":

var N = JSON.Parse(yourJsonString);
string price = N["USD"]["15m"].Value;
Debug.Log(price);

Щоб отримати властивість ISK "15 м":

var N = JSON.Parse(yourJsonString);
string price = N["ISK"]["15m"].Value;
Debug.Log(price);

Щоб отримати власність NZD "15 м":

var N = JSON.Parse(yourJsonString);
string price = N["NZD"]["15m"].Value;
Debug.Log(price);

Решта властивостей Json, які не починаються з числової цифри, можна обробляти за допомогою JsonUtility Unity.


4. УСУНЕННЯ НЕСПРАВНОСТЕЙ JsonUtility:

Проблеми при серіалізації з JsonUtility.ToJson?

Отримання порожнього рядка або " {}" з JsonUtility.ToJson?

. Переконайтесь, що клас не є масивом. Якщо це так, використовуйте допоміжний клас вище з JsonHelper.ToJsonзамість JsonUtility.ToJson.

B . Додайте [Serializable]до верхньої частини класу, який ви серіалізуєте.

C . Видаліть властивість із класу. Наприклад, у змінній public string playerId { get; set; } видаліть { get; set; } . Єдність не може серіалізувати це.

Проблеми при десериалізації з JsonUtility.FromJson?

. Якщо ви отримаєте Null, переконайтеся, що Json не є масивом Json. Якщо це так, використовуйте допоміжний клас вище з JsonHelper.FromJsonзамість JsonUtility.FromJson.

B . Якщо ви отримуєте NullReferenceExceptionпід час десеріалізації, додайте [Serializable]до верхньої частини класу.

C. Будь-які інші проблеми, переконайтеся, що ваш json дійсний. Перейдіть на цей сайт тут і вставте json. Він повинен показати вам, чи є json дійсним. Він також повинен генерувати належний клас за допомогою Json. Просто переконайтеся, що ви видаляєте remove { get; set; } з кожної змінної, а також додаєте [Serializable]вгору кожного згенерованого класу.


Newtonsoft.Json:

Якщо з якихось причин потрібно використовувати Newtonsoft.Json, перевірте тут роздільну версію Unity . Зверніть увагу, що при використанні певної функції може статися збій. Будь обережний.


Щоб відповісти на ваше запитання :

Оригінальні дані є

 [{"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] це ваші треті дані

або дані всередині класу з playerInstance[0].playerLoc, playerInstance[1].playerLoc, playerInstance[2].playerLoc......

Ви можете використовувати, playerInstance.Lengthщоб перевірити довжину, перш ніж отримати доступ до неї.

ПРИМІТКА: Вилучити { get; set; } з playerкласу. Якщо у вас є { get; set; }, це не спрацює. Юніті JsonUtilityце НЕ працювати з членами класу , які визначаються як властивості .


Я повертаю масив рядків mysqlзапиту з PHP за допомогою json_encode($row). Отже, відповідь складається з декількох JSONObjects у форматі [{"playerId":"1","playerLoc":"Powai"},{"playerId":"2","playerLoc":"Andheri"},{"playerId":"3","playerLoc":"Churchgate"}]. Я спробував JsonUtility, але не зміг десеріалізувати об'єкти та отримати окремі об'єкти json. Якщо ви можете мені в цьому допомогти.
dil33pm

2
Подивіться на код, який я розмістив вище. Він показує вам три способи зробити це за допомогою JsonUtility.FromJson. Я забув сказати вам прибрати { get; set; }з playerкласу. Якщо у вас є { get; set; }, це не спрацює. Порівняйте свій playerклас із тим, який я розмістив вище, і ви зрозумієте, про що я кажу.
Програміст,

1
Нема проблем. Я відредагую це, коли Unity додасть підтримку масиву (що дуже скоро), тому вам більше не буде потрібно цей клас Helper.
Програміст

1
Я б не зайшов так далеко, щоб сказати "Забудь про всі сторонні бібліотеки". JsonUtility має обмеження. Він не повертає об'єкт JSON, над яким ви можете виконувати дії. Наприклад, я отримую файл json і хочу перевірити, чи є ключ "успіху". Не можу. JsonUtility вимагає, щоб споживач знав точний вміст файлу json. Крім того, немає перетворювача словників. Отже, він робить деякі хороші речі, але використання сторонніх виробників все ще потрібно.
Еверт

2
Використовуйте JsonHelper. Це добре. Якщо ви створюєте Json з ним, ви також можете читати json з ним без додаткових кроків. Єдиний раз, коли вам може знадобитися зробити додаткові речі, це якщо ви отримуєте масив json від сервера, і що включено в рішення, - це моя відповідь. Інший спосіб без JsonHelper- помістити клас в інший клас, а потім зробити його a List. Це працює для більшості людей. Якщо ви шукаєте спосіб збереження та завантаження ігрових даних, то перегляньте це . Ви завантажуєте та зберігаєте одним рядком коду.
Програміст

17

Припустимо, ви отримали такий файл JSON

[
    {
        "type": "qrcode",
        "symbol": [
            {
                "seq": 0,
                "data": "HelloWorld9887725216",
                "error": null
            }
        ]
    }
]

Щоб проаналізувати вищезазначений JSON в одиниці, ви можете створити модель JSON, як ця.

[System.Serializable]
public class QrCodeResult
{
    public QRCodeData[] result;
}

[System.Serializable]
public class Symbol
{
    public int seq;
    public string data;
    public string error;
}

[System.Serializable]
public class QRCodeData
{
    public string type;
    public Symbol[] symbol;
}

А потім просто проаналізуйте наступним чином ...

var myObject = JsonUtility.FromJson<QrCodeResult>("{\"result\":" + jsonString.ToString() + "}");

Тепер ви можете змінити JSON / CODE відповідно до ваших потреб. https://docs.unity3d.com/Manual/JSONSerialization.html


Це насправді працює досить добре, змусило його працювати з таким класом, як Symbol, який також не є масивом.
Геннон

4
Я намагаюся це з єдністю 2018, але це не працює: масиви не розібрані
Jean-Michaël Celerier

Використовується в єдності 2018 та 2019 рр. Чудово працює. Отримайте доступ до даних масиву, наприклад: Debug.Log(myObject.result[0].symbol[0].data);або for(var i = 0; i <= myObject.result.Length - 1; i ++) { Debug.Log(myObject.result[i].type); }абоforeach (var item in myObject.result) { Debug.Log(item.type); }
Towerss

@Narottam Goyal, у деяких випадках ваш метод не працює, це також дуже складне рішення для початківців.
Зверніться

@JunedKhanMomin Ваша відповідь робить в основному те саме, але не враховуючи той факт, що це питання тут стосувалося конкретного масиву кореневого рівня в даних JSON. Загалом, вам краще скористатися відповіддю програміста, яка є набагато більш докладною.
derHugo

2

вам потрібно додати [System.Serializable]до PlayerItemкласу, наприклад:

using System;
[System.Serializable]
public class PlayerItem   {
    public string playerId;
    public string playerLoc;
    public string playerNick;
}

0

Не обрізайте, []і у вас повинно бути добре. []визначте масив JSON, який саме те, що вам потрібно, щоб мати можливість ітерації його елементів.


0

Як сказав @Maximiliangerhardt, MiniJson не має можливості десериалізувати належним чином. Я використовував JsonFx і працює як шарм. Працює з[]

player[] p = JsonReader.Deserialize<player[]>(serviceData);
Debug.Log(p[0].playerId +" "+ p[0].playerLoc+"--"+ p[1].playerId + " " + p[1].playerLoc+"--"+ p[2].playerId + " " + p[2].playerLoc);

0

Щоб прочитати файл JSON, зверніться до цього простого прикладу

Ваш файл JSON (StreamingAssets / Player.json)

{
    "Name": "MyName",
    "Level": 4
}

С # сценарій

public class Demo
{
    public void ReadJSON()
    {
        string path = Application.streamingAssetsPath + "/Player.json";
        string JSONString = File.ReadAllText(path);
        Player player = JsonUtility.FromJson<Player>(JSONString);
        Debug.Log(player.Name);
    }
}

[System.Serializable]
public class Player
{
    public string Name;
    public int Level;
}

1
А як щодо масивів? І що додає ця відповідь, яка не була позначена вже у прийнятій відповіді більше 3 років тому?
derHugo

0

Ви можете використовувати Newtonsoft.Jsonпросто додати Newtonsoft.dllдо свого проекту і використовувати сценарій нижче

using System;
using Newtonsoft.Json;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{

    [Serializable]
    public class Person
    {
        public string id;
        public string name;
    }
    public Person[] person;

    private void Start()
    {
       var myjson = JsonConvert.SerializeObject(person);

        print(myjson);

    }
}

введіть тут опис зображення

інше рішення - використання JsonHelper

using System;
using Newtonsoft.Json;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{

    [Serializable]
    public class Person
    {
        public string id;
        public string name;
    }
    public Person[] person;

    private void Start()
    {
        var myjson = JsonHelper.ToJson(person);

        print(myjson);

    }
}

введіть тут опис зображення


0

ЯКЩО ви використовуєте Vector3, це те, що я зробив

1 - Я створюю клас Ім'я it Player

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[Serializable]
public class Player
{
    public Vector3[] Position;

}

2 - тоді я називаю це так

if ( _ispressed == true)
        {
            Player playerInstance = new Player();
            playerInstance.Position = newPos;
            string jsonData = JsonUtility.ToJson(playerInstance);

            reference.Child("Position" + Random.Range(0, 1000000)).SetRawJsonValueAsync(jsonData);
            Debug.Log(jsonData);
            _ispressed = false;
        }

3- і це результат

"Позиція": [{"x": - 2.8567452430725099, "y": - 2.4323320388793947, "z": 0.0}]}}


0

Єдність <= 2019

Нароттам Гоял мав гарну ідею обернути масив у об'єкт json, а потім десеріалізувати у структуру. Далі використовується Generics, щоб вирішити це для масивів будь-якого типу, на відміну від створення кожного класу кожного разу.

[System.Serializable]
private struct JsonArrayWrapper<T> {
    public T wrap_result;
}

public static T ParseJsonArray<T>(string json) {
    var temp = JsonUtility.FromJson<JsonArrayWrapper<T>>("{\" wrap_result\":" + json + "}");
    return temp.wrap_result;
}

Його можна використовувати наступним чином:

string[] options = ParseJsonArray<string[]>(someArrayOfStringsJson);

Єдність 2020

У Unity 2020 є офіційний пакет newtonsoft, який є набагато кращою бібліотекою json.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.