在Unity中序列化和反序列化Json和Json数组


96

我有一个从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; }
}

修剪后,[]我能够使用MiniJSON解析json。但是它只返回第一个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));
}

谢谢!


为什么要除去外部[]?这就是它的清单。只是停止删除它,并将其反序列化为数组或列表,我希望它会很好。请发布您尝试过的代码。
乔恩·斯基特

向我们展示用于反序列化的类。格式很奇怪,为什么第二个playerId不能用大括号括起来?它应该反序列化为诸如的列表List<PlayerLocation>,因为这一个数组。
Maximilian Gerhardt

@MaximilianGerhardt对不起,花括号是一个错字。在问题中修复它并添加了代码。谢谢。
dil33pm '16

1
我认为您对此库处理反序列化的理解有问题。这不是通常的反序列化(如您所见Newtonsoft.Json),但Json.Deserialize()始终会IDictionary<string,object>向您返回a ,然后您继续操作List<object>。查看stackoverflow.com/a/22745634/5296568。最好获得一个更好的JSON反序列化器,该反序列化器会执行您习惯的反序列化。
Maximilian Gerhardt

我尝试过的@MaximilianGerhardt IDictionary<string,object>。我能够获得价值,但只有第一价值KeyValuePair<>
dil33pm

Answers:


244

5.3.3更新后,Unity将JsonUtility添加到其API中。除非您正在做更复杂的事情,否则请不要理会所有第三方库。JsonUtility比其他Json库快。更新到Unity 5.3.3版本或更高版本,然后尝试以下解决方案。

JsonUtility是轻量级的API。仅支持简单类型。它支持的集合,如字典。一种例外是List。它支持ListList数组!

如果您需要序列化a Dictionary或执行其他操作而不是简单地序列化和反序列化简单数据类型,请使用第三方API。否则,请继续阅读。

要序列化的示例类:

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

1.一个数据对象(非数组JSON)

序列化A部分

使用该public static string ToJson(object obj);方法序列化到Json 。

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部分

使用public static string ToJson(object obj, bool prettyPrint);方法重载序列化到Json 。只需传递trueJsonUtility.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"
}

反序列化A部分

使用public static T FromJson(string json);方法重载反序列化 json 。

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

B部分反序列化

使用public static object FromJson(string json, Type type);方法重载反序列化 json 。

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

反序列化C部分

使用public static void FromJsonOverwrite(string json, object objectToOverwrite);方法反序列化 json 。当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出现了不止一次。Unity 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 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);

输出

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

反序列化Json Array

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的JsonUtility不支持此功能,因为“ 15m”属性以数字开头。类变量不能以整数开头。

SimpleJSON.cs从Unity的wiki下载。

要获取USD的“ 15m”属性:

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

要获得ISK的“ 15m”属性:

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

要获得NZD的“ 15m”属性:

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

其余以数字开头的Json属性可以由Unity的JsonUtility处理。


4.麻烦的JsonUtility:

使用时的问题JsonUtility.ToJson

获取空字符串或“ {}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]到该类的顶部。

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].playerLocplayerInstance[1].playerLocplayerInstance[2].playerLoc......

您可以playerInstance.Length在访问前检查长度。

注意: 从课程中删除 。如果您有,它将无法正常工作。Unity的确实与被定义为类成员的工作性质{ 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添加对array的支持时(很快),我将对其进行编辑,因此您将不再需要该Helper类。
程序员

1
我不会说“忘记所有第三方图书馆”。JsonUtility有局限性。它不会返回可在其上执行操作的JSON对象。例如,我得到一个json文件,并想检查“成功”键是否可用。不能做 JsonUtility要求使用者知道json文件的确切内容。另外,没有字典转换器。因此,它做了一些好事,但仍然需要使用第三方。
Everts

2
使用JsonHelper。没关系。如果使用它创建Json,则还可以使用它读取json,而无需执行其他步骤。您可能唯一需要做的事情是,如果您从服务器接收到json数组,而解决方案中包含的内容在我的回答中。另一种不用的方法JsonHelper是将该类放在另一个类中,然后使其成为一个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类一起使用。
Gennon '18

4
我与统一2018尝试这一点,但这个不起作用:数组不被解析
吉恩-迈克尔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,您的方法在某些情况下不起作用,对于初学者来说也是非常困难的解决方案,请在此线程链接
Juned Khan Momin

@JunedKhanMomin您的答案基本相同,但是没有解决以下问题:特定于JSON数据的根级数组。通常,您应该参考程序员的答案,该答案会更加详细。
derHugo

2

您必须像这样添加[System.Serializable]PlayerItem类中:

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


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
}

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

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-我创建一个类命名为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

Unity <= 2019

Narottam Goyal有一个很好的想法,将数组包装在json对象中,然后反序列化为结构。下面使用泛型为所有类型的数组解决此问题,而不是每次都创建一个新类。

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

Unity 2020

Unity 2020中有一个官方的newtonsoft软件包,它是一个更好的json库。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.