JsonUtility
유니티 5.3 이상부터 JsonUtility를 지원한다. 외부 JSON 라이브러리가 필요 없다. < JsonUtility 멤버 함수 >
1. 기본 사용법Enemy에서는 public을 사용하지 않고 SerializeField를 이용했다.코드가 복잡하지 않기 때문에 자세한 설명은 생략한다.
using UnityEngine;
using System; using System.Collections.Generic; [Serializable] public class Enemy { [SerializeField] string name; [SerializeField] List<string> skills; public Enemy(string name, List<string> skills) { this.name = name; this.skills = skills; } } //Test string jsonStr = JsonUtility.ToJson(new Enemy("오거", new List<string>() { "물리공격", "마법" })); Debug.Log(jsonStr); Enemy enemy0 = JsonUtility.FromJson<Enemy>(jsonStr); Enemy enemy1 = new Enemy("empty", new List<string>() { }); JsonUtility.FromJsonOverwrite(jsonStr, enemy1); 결과) jsonStr의 출력 결과는 다음과 같다. {"name":"오거","skills":["물리공격","마법"]} enemy1 값이 오거, 물리공격, 마법으로 변경된다. 2. List 직렬화다음과 같이 List를 Json으로 변환하면 빈 문자열만 변환한다.
var enemies = new List<Enemy>();
enemies.Add(new Enemy("오거", new List<string>() { "물리공격", "마법" })); enemies.Add(new Enemy("트롤", new List<string>() { "공격", "회복", "부활" })); string emptyStr = JsonUtility.ToJson(enemies); List는 별도로 직렬화 시켜 주어야 한다.
[Serializable]
public class Serialization<T> { [SerializeField] List<T> target; public List<T> ToList() { return target; } public Serialization(List<T> target) { this.target = target; } } //Test var enemies = new List<Enemy>(); enemies.Add(new Enemy("오거", new List<string>() { "물리공격", "마법" })); enemies.Add(new Enemy("트롤", new List<string>() { "공격", "회복", "부활" })); string str = JsonUtility.ToJson(new Serialization<Enemy>(enemies)); Debug.Log(str); List<Enemy> retEnemies = JsonUtility.FromJson<Serialization<Enemy>>(str).ToList(); 결과) str의 출력 결과는 다음과 같다. {"target":[{"name":"오거","skills":["물리공격","마법"]},{"name":"트롤","skills":[" 공격","회복","부활"]}]} retEnemies에 값을 넣는다. 3. Dictionary 직렬화
[Serializable]
public class Serialization<TKey, TValue> : ISerializationCallbackReceiver { [SerializeField] List<TKey> keys; [SerializeField] List<TValue> values; Dictionary<TKey, TValue> target; public Dictionary<TKey, TValue> ToDictionary() { return target; } public Serialization(Dictionary<TKey, TValue> target) { this.target = target; } public void OnBeforeSerialize() { keys = new List<TKey>(target.Keys); values = new List<TValue>(target.Values); } public void OnAfterDeserialize() { var count = Math.Min(keys.Count, values.Count); target = new Dictionary<TKey, TValue>(count); for (var i = 0; i < count; ++i) { target.Add(keys[i], values[i]); } } } //Test var enemies = new Dictionary<int, Enemy>(); enemies.Add(1000, new Enemy("오거", new List<string>() { "물리공격", "마법" })); enemies.Add(1001, new Enemy("트롤", new List<string>() { "공격", "회복", "부활" })); string str = JsonUtility.ToJson(new Serialization<int, Enemy>(enemies)); Debug.Log(str); Dictionary<int, Enemy> retEemies = JsonUtility.FromJson<Serialization<int, Enemy>>(str).ToDictionary(); 결과) str의 출력 결과는 다음과 같다. {"keys":[1000,1001],"values":[{"name":"오거","skills":["물리공격","마법"]}, {"name":"트롤","skills":["공격","회복","부활"]}]} ISerializationCallbackReceiver로 직렬화 기능확장Unity 5.3에서 "UnityEngine.ISerializationCallbackReceiver"가 추가 되었다.ISerializationCallbackReceiver를 상속하면 직렬화(ToJson)전에 OnBeforeSerialize가 호출되고 역직렬화(FromJson)전에 OnAfterDeserialize가 호출된다.
public class Character : ISerializationCallbackReceiver
{ public void OnBeforeSerialize () { Debug.Log ( "OnBeforeSerialize" ); } public void OnAfterDeserialize () { Debug.Log ( "OnAfterDeserialize" ); } } 참조) http://kou-yeung.hatenablog.com/entry/2015/12/31/014611 https://docs.unity3d.com/ScriptReference/JsonUtility.html https://docs.unity3d.com/ScriptReference/ISerializationCallbackReceiver.html |
유니티 5.3버전부터 JSON 포맷의 데이터를 파싱할 수 있는 API가 추가되었다. 플러그인을 추가하지않고, 내장된 API로만 구현을 하는 게 좋겠다싶어 조금 알아봤지만 만족스럽진 않았다.
일단 예제를 만들어서 테스트해보자.
준비1
Serialize할 수 있는 간단한 데이터 포맷 클래스를 만든다.
[Serializable]
public class PlayerInfo
{
public string name;
public int lives;
public float strength;
}
준비2
JsonHelper 클래스를 따로 만들어준다. JsonUtility의 가장 큰 단점이 배열을 받을 수 없다는 것인데 다음과 같이 JsonObject를 감싸는(JsonObject 배열을 가지는) Wrapper 클래스를 따로 만들어서 문제를 해결할 수 있다.
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;
}
}
테스트 코드
using UnityEngine;
using UnityEditor;
using System;
using System.IO;
using System.Collections.Generic;
public class JsonTestWindow : EditorWindow {
[MenuItem("JSON/Test 1")]
static void Init()
{
JsonTestWindow window = (JsonTestWindow)EditorWindow.GetWindow(typeof(JsonTestWindow));
window.Show();
}
void OnGUI()
{
if (GUILayout.Button ("Save")) {
Save ();
}
if (GUILayout.Button ("Load")) {
Load ();
}
}
void Save()
{
//...아래에서
}
void Load()
{
//...//아래에서
}
}
Save()
void Save()
{
PlayerInfo[] playerInfo = new PlayerInfo[2];
playerInfo[0] = new PlayerInfo();
playerInfo[0].name = "12341234";
playerInfo[0].lives = 5;
playerInfo[0].strength = 25.0f;
playerInfo[1] = new PlayerInfo();
playerInfo[1].name = "1235235";
playerInfo[1].lives = 3;
playerInfo[1].strength = 30.2f;
string toJson = JsonHelper.ToJson(playerInfo, prettyPrint:true);
File.WriteAllText (Application.dataPath + "/Saves/data.json", toJson);
}
Load()
void Load()
{
string jsonString = File.ReadAllText (Application.dataPath + "/Saves/data.json");
var data = JsonHelper.FromJson <PlayerInfo>(jsonString);
foreach (var playerInfo in data) {
Debug.Log (playerInfo.name);
Debug.Log (playerInfo.lives);
Debug.Log (playerInfo.strength);
}
}
편의를 위해서 EditorWindow로 테스트 해보았다.
별다른 플러그인 없이 쉽게 구현이 가능하다는 장점이 있지만, 인덱싱이 안되고 데이터포맷을 미리 알지못하면 처리할 수 없다는 문제가 있으므로 간단한 데이터 저장용 외에는 활용하기 어려울 것 같다.
[JsonUtility에서 List <T>와 Dictionary <TKey, TValue> 직렬화]
[JsonUtility에서 List <T>와 Dictionary <TKey, TValue> 직렬화]
직렬화 대상이되기 위해서는 다음의 조건이있다
1. 클래스에 [Serializable] 속성 2.private 멤버 변수에 [SerializeField]의 특성 3.public 멤버 변수로
물론, List와 Dictionary은 이상의 조건에 부합하지 않는
예를 들어 다음 클래스를 사용합니다
using UnityEngine; using System; using System.Collections.Generic; [Serializable] public class Enemy { [SerializeField] string name; [SerializeField] List < string > skills; public Enemy ( string name List < string > skills) { this .name = name; this .skills = skills; } }
var enemies = new List <Enemy> (); enemies.Add ( new Enemy ( "슬라임" , new List < string > () { "공격" })); enemies.Add ( new Enemy ( "킹 슬라임" , new List < string > () { "공격" , "회복" })); Debug.Log (JsonUtility.ToJson (enemies)); // 출력 : {}
써 보았다
// Serialization.cs using UnityEngine; using System.Collections; using System.Collections.Generic; using System; // List <T> [Serializable] public class Serialization <T> { [SerializeField] List <T> target; public List <T> ToList () { return target;} public Serialization (List <T> target) { this .target = target; } } // Dictionary <TKey, TValue> [Serializable] public class Serialization <TKey, TValue> : ISerializationCallbackReceiver { [SerializeField] List <TKey> keys; [SerializeField] List <TValue> values; Dictionary <TKey, TValue> target; public Dictionary <TKey, TValue> ToDictionary () { return target;} public Serialization (Dictionary <TKey, TValue> target) { this .target = target; } public void OnBeforeSerialize () { keys = new List <TKey> (target.Keys); values = new List <TValue> (target.Values); } public void OnAfterDeserialize () { var count = Math.Min (keys.Count, values.Count); target = new Dictionary <TKey, TValue> (count); for (var i = 0 ; i <count; ++ i) { target.Add (keys [i], values [i]); } } }
사용법
// List <T> -> Json 문자열 (예 : List <Enemy>) string str = JsonUtility.ToJson ( new Serialization <Enemy> (enemies)); // 출력 : { "target": [{ "name ":"슬라임 ","skills ":"공격 "]}, {"name ":"킹 슬라임 ","skills ":"공격 ","회복 "]}]} // Json 문자열 -> List <T> List <Enemy> enemies = JsonUtility.FromJson <Serialization <Enemy >> (str) .ToList (); // Dictionary <TKey, TValue> -> Json 문자열 (예 : Dictionary <int, Enemy>) string str = JsonUtility.ToJson ( new Serialization < int , Enemy> (enemies)); // 출력 : { "keys ": [1000,2000]"values ": [{"name ":"슬라임 ","skills ":"공격 "]}, {"name ":"킹 슬라임 ","skills ":"공격 ","회복 "]}]} // Json 문자열 -> Dictionary <TKey, TValue> Dictionary < int , Enemy> enemies = JsonUtility.FromJson <Serialization < int , Enemy >> (str) .ToDictionary ();
게다가 BitArray도 써보기로했다
// BitArray [Serializable] public class SerializationBitArray : ISerializationCallbackReceiver { [SerializeField] string flags; BitArray target; public BitArray ToBitArray () { return target;} public SerializationBitArray (BitArray target) { this .target = target; } public void OnBeforeSerialize () { var ss = new System.Text.StringBuilder (target.Length); for (var i = 0 ; i <target.Length; ++ i) { ss.Insert ( 0 , target [i]? '1' : '0' ); } flags = ss.ToString (); } public void OnAfterDeserialize () { target = new BitArray (flags.Length); for (var i = 0 ; i <flags.Length; ++ i) { target.Set (flags.Length - i - 1 , flags [i] == '1' ); } } }
사용법
BitArray bits = new BitArray ( 4 ); bits.Set ( 1 , true ); // BitArray -> Json 문자열 var str = JsonUtility.ToJson ( new SerializationBitArray (bits)); // 출력 : { "flags": "0010"} // Json 문자열 -> BitArray BitArray bits = JsonUtility.FromJson <SerializationBitArray> (s) .ToBitArray ();
using UnityEngine; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System; | |
[Serializable] | |
public class Serialization<T> | |
{ | |
[SerializeField] | |
List<T> target; | |
public List<T> ToList() { return target; } | |
public Serialization(List<T> target) | |
{ | |
this.target = target; | |
} | |
} | |
[Serializable] | |
public class Serialization<TKey, TValue> : ISerializationCallbackReceiver | |
{ | |
[SerializeField] | |
List<TKey> keys; | |
[SerializeField] | |
List<TValue> values; | |
Dictionary<TKey, TValue> target; | |
public Dictionary<TKey, TValue> ToDictionary() { return target; } | |
public Serialization(Dictionary<TKey, TValue> target) | |
{ | |
this.target = target; | |
} | |
public void OnBeforeSerialize() | |
{ | |
keys = new List<TKey>(target.Keys); | |
values = new List<TValue>(target.Values); | |
} | |
public void OnAfterDeserialize() | |
{ | |
var count = Math.Min(keys.Count, values.Count); | |
target = new Dictionary<TKey, TValue>(count); | |
for (var i = 0; i < count; ++i) | |
{ | |
target.Add(keys[i], values[i]); | |
} | |
} | |
} | |
[Serializable] | |
public class SerializationBitArray : ISerializationCallbackReceiver | |
{ | |
[SerializeField] | |
string flags; | |
BitArray target; | |
public BitArray ToBitArray() { return target; } | |
public SerializationBitArray(BitArray target) | |
{ | |
this.target = target; | |
} | |
public void OnBeforeSerialize() | |
{ | |
var ss = new System.Text.StringBuilder(target.Length); | |
for(var i = 0 ; i < target.Length ; ++i) | |
{ | |
ss.Insert(0, target[i]?'1':'0'); | |
} | |
flags = ss.ToString(); | |
} | |
public void OnAfterDeserialize() | |
{ | |
target = new BitArray(flags.Length); | |
for (var i = 0; i < flags.Length; ++i) | |
{ | |
target.Set(flags.Length - i - 1, flags[i] == '1'); | |
} | |
} | |
} |
public 을 사용하지않고 SerializeField를 사용했다는 말은 무슨 뜻입니까?
답글삭제private 속성을 유니티 인스펙터에서 사용할 때 사용합니다.
삭제알쓸다잡 (알아두면 쓸데있는 다양한 잡식): Unity - Local Db - C - Jsonutility >>>>> Download Now
답글삭제>>>>> Download Full
알쓸다잡 (알아두면 쓸데있는 다양한 잡식): Unity - Local Db - C - Jsonutility >>>>> Download LINK
>>>>> Download Now
알쓸다잡 (알아두면 쓸데있는 다양한 잡식): Unity - Local Db - C - Jsonutility >>>>> Download Full
>>>>> Download LINK