Serializar e desserializar array Json e Json no Unity

96

Eu tenho uma lista de itens enviados de um arquivo PHP para a unidade usando WWW.

Os WWW.textparece:

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

Onde eu aparo o extra []do string. Quando tento analisá-lo usando Boomlagoon.JSON, apenas o primeiro objeto é recuperado. Descobri que tenho que deserialize()entrar na lista e importei o MiniJSON.

Mas estou confuso quanto a deserialize()esta lista. Quero fazer um loop por cada objeto JSON e recuperar dados. Como posso fazer isso no Unity usando C #?

A classe que estou usando é

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

Depois de aparar o [], consigo analisar o json usando o MiniJSON. Mas está retornando apenas o primeiro 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));
}

Obrigado!

dil33pm
fonte
Por que você removeu o externo [e ]? Isso é o que o torna uma lista. Apenas pare de removê-lo e desserialize-o como um array ou uma lista e eu espero que tudo funcione. Por favor, poste o código que você tentou.
Jon Skeet
Mostre-nos a classe usada para desserialização. O formato é estranho, por que o segundo playerId não está entre chaves? Ele deve desserializar para uma lista de algo, como List<PlayerLocation>, porque este é um array.
Maximilian Gerhardt
@MaximilianGerhardt Desculpe, as chaves foram um erro de digitação. Corrigido na questão e adicionado o código também. Obrigado.
dil33pm
1
Acho que há algo errado com o seu entendimento de como esta biblioteca aqui lida com a desserialização. Não é a desserialização usual (como talvez você tenha visto Newtonsoft.Json), mas Json.Deserialize()SEMPRE retorna a IDictionary<string,object>para você e você opera List<object>. Consulte stackoverflow.com/a/22745634/5296568 . De preferência, obtenha um desserializador JSON melhor que faça a desserialização a que você está acostumado.
Maximilian Gerhardt
@MaximilianGerhardt eu tentei IDictionary<string,object>. Consigo extrair o valor, mas apenas o primeiro KeyValuePair<>.
dil33pm

Respostas:

244

A Unity adicionou JsonUtility à API após a atualização 5.3.3 . Esqueça todas as bibliotecas de terceiros, a menos que esteja fazendo algo mais complicado. JsonUtility é mais rápido do que outras bibliotecas Json. Atualize para a versão Unity 5.3.3 ou superior e tente a solução abaixo.

JsonUtilityé uma API leve. Apenas tipos simples são suportados. Ele não oferece suporte a coleções como Dicionário. Uma exceção é List. Ele suporta Liste Listarray!

Se você precisar serializar Dictionaryou fazer algo diferente de simplesmente serializar e desserializar tipos de dados simples, use uma API de terceiros. Caso contrário, continue lendo.

Classe de exemplo para serializar:

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

1. UM OBJETO DE DADOS (NON-ARRAY JSON)

Serializando a Parte A :

Serialize para Json com o public static string ToJson(object obj);método.

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

Produto :

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

Serializando a Parte B :

Serialize para Json com a public static string ToJson(object obj, bool prettyPrint);sobrecarga do método. Basta passar truepara a JsonUtility.ToJsonfunção para formatar os dados. Compare a saída abaixo com a saída acima.

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

Produto :

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

Desserializando a Parte A :

Desserialize json com a public static T FromJson(string json);sobrecarga do método.

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

Desserializando a Parte B :

Desserialize json com a public static object FromJson(string json, Type type);sobrecarga do método.

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

Desserializando a Parte C :

Desserialize json com o public static void FromJsonOverwrite(string json, object objectToOverwrite);método. Quando JsonUtility.FromJsonOverwriteé usado, nenhuma nova instância daquele Objeto que você está desserializando será criada. Ele simplesmente reutilizará a instância que você passou e sobrescreverá seus valores.

Isso é eficiente e deve ser usado, se possível.

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. MÚLTIPLOS DADOS (ARRAY JSON)

Seu Json contém vários objetos de dados. Por exemplo, playerIdapareceu mais de uma vez . O Unity JsonUtilitynão suporta array, pois ainda é novo, mas você pode usar uma classe auxiliar desta pessoa para fazer o array funcionar JsonUtility.

Crie uma classe chamada JsonHelper. Copie o JsonHelper diretamente de baixo.

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;
    }
}

Serializando Json Array :

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

Produto :

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

Desserializando o array 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);

Produto :

Powai

User2


Se esta for uma matriz Json do servidor e você não a criou manualmente :

Você pode ter que adicionar {"Items":na frente da string recebida e adicionar }no final dela.

Fiz uma função simples para isso:

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

então você pode usá-lo:

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

3. Deserializar string json sem classe e Deserializar Json com propriedades numéricas

Este é um Json que começa com um número ou propriedades numéricas.

Por exemplo:

{ 
"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" : "$"}
}

O Unity JsonUtilitynão suporta isso porque a propriedade "15m" começa com um número. Uma variável de classe não pode começar com um inteiro.

Baixe SimpleJSON.csna wiki do Unity .

Para obter a propriedade "15m" de USD:

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

Para obter a propriedade "15m" da ISK:

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

Para obter a propriedade "15m" do NZD:

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

O restante das propriedades Json que não começam com um dígito numérico podem ser manipuladas pelo JsonUtility do Unity.


4. RESOLUÇÃO DE PROBLEMAS JsonUtility:

Problemas ao serializar com JsonUtility.ToJson?

Obtendo uma string vazia ou " {}" com JsonUtility.ToJson?

Um . Certifique-se de que a classe não seja uma matriz. Se for, use a classe auxiliar acima com em JsonHelper.ToJsonvez de JsonUtility.ToJson.

B . Adicione [Serializable]ao início da classe que você está serializando.

C . Remova a propriedade da classe. Por exemplo, na variável, public string playerId { get; set; } remova { get; set; } . O Unity não pode serializar isso.

Problemas ao desserializar com JsonUtility.FromJson?

Um . Se você obtiver Null, certifique-se de que o Json não seja um array Json. Se for, use a classe auxiliar acima com em JsonHelper.FromJsonvez de JsonUtility.FromJson.

B . Se você conseguir NullReferenceExceptionao desserializar, [Serializable]entre no topo da classe.

C. Quaisquer outros problemas, verifique se seu json é válido. Vá até este site aqui e cole o json. Deve mostrar se o json é válido. Ele também deve gerar a classe adequada com o Json. Apenas certifique-se de remover remove { get; set; } de cada variável e também adiciona [Serializable]ao topo de cada classe gerada.


Newtonsoft.Json:

Se por algum motivo for necessário usar o Newtonsoft.Json , verifique a versão bifurcada do Unity aqui . Observe que você pode travar se determinado recurso for usado. Seja cuidadoso.


Para responder à sua pergunta :

Seus dados originais são

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

Adicionar {"Items": na frente dele, em seguida, adicione } no final do mesmo.

Código para fazer isso:

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

Agora você tem:

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

Para serializar os vários dados do php como matrizes , agora você pode fazer

public player[] playerInstance;
playerInstance = JsonHelper.FromJson<player>(serviceData);

playerInstance[0] são seus primeiros dados

playerInstance[1] são seus segundos dados

playerInstance[2] é o seu terceiro dado

ou dados dentro da classe com playerInstance[0].playerLoc, playerInstance[1].playerLoc, playerInstance[2].playerLoc......

Você pode usar playerInstance.Lengthpara verificar o comprimento antes de acessá-lo.

NOTA: Remova { get; set; } da playerclasse. Se você tiver { get; set; }, não funcionará. Unidade de JsonUtilityque não trabalhar com os alunos que são definidas como propriedades .

Programador
fonte
Estou retornando uma matriz de linhas de uma mysqlconsulta de PHP usando json_encode($row). Portanto, a resposta consiste em vários JSONObjects no formato [{"playerId":"1","playerLoc":"Powai"},{"playerId":"2","playerLoc":"Andheri"},{"playerId":"3","playerLoc":"Churchgate"}]. Tentei o JsonUtility, mas não consegui desserializar os objetos e obter objetos json individuais. Se você pode me ajudar com isso.
dil33pm
2
Olhe o código que postei acima. Mostra três maneiras de fazer isso JsonUtility.FromJson. Esqueci de dizer para você { get; set; }sair da playeraula. Se você tiver { get; set; }, não vai funcionar. Compare sua playeraula com a que postei acima e você entenderá o que estou dizendo.
Programador
1
Sem problemas. Vou editar isso quando o Unity adicionar suporte para array (o que é muito em breve), então você não precisará mais dessa classe Helper.
Programador
1
Eu não iria tão longe a ponto de dizer "Esqueça todas as bibliotecas de terceiros". JsonUtility tem limitações. Ele não retorna um objeto JSON no qual você pode executar ações. Por exemplo, recebo um arquivo json e desejo verificar se uma chave de "sucesso" está disponível. Não posso fazer. O JsonUtility requer que o consumidor saiba o conteúdo exato do arquivo json. Além disso, nenhum conversor de Dicionário. Portanto, ele faz algumas coisas boas, mas o uso de terceiros ainda é necessário.
Vence em
2
Use JsonHelper. Está bem. Se você criar Json com ele, também poderá ler json com ele sem etapas extras. A única vez que você pode precisar fazer coisas extras é se estiver recebendo o array json do servidor e isso está incluído na solução na minha resposta. Outra maneira JsonHelperé colocar a classe em outra classe e torná-la uma List. Isso tem funcionado para a maioria das pessoas. Se você está procurando uma maneira de salvar e carregar dados do jogo, veja isto . Você carrega e salva com uma linha de código.
Programador
17

Suponha que você tenha um JSON como este

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

Para analisar o JSON acima na unidade, você pode criar um modelo JSON como este.

[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;
}

E então simplesmente analise da seguinte maneira ...

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

Agora você pode modificar o JSON / CODE de acordo com sua necessidade. https://docs.unity3d.com/Manual/JSONSerialization.html

Narottam Goyal
fonte
Isso realmente funciona muito bem, funcionou com uma classe como Symbol, que também não é um array.
Gennon
4
Estou tentando fazer isso com o Unity 2018, mas não funciona: os arrays não são analisados
Jean-Michaël Celerier
Usado na unidade 2018 e 2019. Funciona muito bem. Acesse os dados da matriz como: Debug.Log(myObject.result[0].symbol[0].data);ou for(var i = 0; i <= myObject.result.Length - 1; i ++) { Debug.Log(myObject.result[i].type); }ouforeach (var item in myObject.result) { Debug.Log(item.type); }
Towerss
@Narottam Goyal, seu método não está funcionando em alguns casos, também é uma solução muito difícil para iniciantes, consulte esta resposta minha neste link do
Juned Khan Momin
@JunedKhanMomin sua resposta faz basicamente o mesmo, mas sem abordar o fato de que esta questão aqui era sobre uma matriz de nível raiz nos dados JSON em específico. Em geral, você deve consultar a resposta do programador, que é muito mais elaborada.
derHugo
2

você tem que adicionar [System.Serializable]à PlayerItemclasse, assim:

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

Não apare o []e você deve ficar bem. []identifique um array JSON que é exatamente o que você precisa para poder iterar seus elementos.

Thomas Hilbert
fonte
0

Como @Maximiliangerhardt disse, o MiniJson não tem a capacidade de desserializar corretamente. Usei o JsonFx e funciona perfeitamente. Funciona com o[]

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);
dil33pm
fonte
0

Para ler o arquivo JSON, consulte este exemplo simples

Seu arquivo JSON (StreamingAssets / Player.json)

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

Script C #

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;
}
Juned Khan Momin
fonte
1
E quanto a matrizes? E o que esta resposta acrescenta que não foi mencionado na resposta aceita de mais de 3 anos atrás?
derHugo 01 de
0

Você pode usar Newtonsoft.Jsonapenas adicionar Newtonsoft.dllao seu projeto e usar o script abaixo

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

    }
}

insira a descrição da imagem aqui

outra solução é usar 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);

    }
}

insira a descrição da imagem aqui

Seyed Morteza Kamali
fonte
0

SE você estiver usando Vector3, isto é o que eu fiz

1- Eu crio uma classe Name it Player

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

}

2- então eu chamo assim

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- e este é o resultado

"Posição": [{"x": - 2,8567452430725099, "y": - 2,4323320388793947, "z": 0,0}]}

Gara
fonte
0

Unidade <= 2019

Narottam Goyal teve uma boa ideia de envolver a matriz em um objeto json e, em seguida, desserializar em uma estrutura. O seguinte usa Genéricos para resolver isso para arrays de todos os tipos, em vez de criar uma nova classe sempre.

[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;
}

Pode ser usado da seguinte maneira:

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

Unity 2020

No Unity 2020, há um pacote oficial da newtonsoft que é uma biblioteca json muito melhor.

Justin Meiners
fonte