언어 정리/c#

c# NewtonSoft.json 활용 데이터 저장 <interface 데이터 추가>

rimugiri 2024. 1. 7. 23:11
728x90

기존 저장방법

https://rimugiri.tistory.com/entry/c-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5-NewtonsoftJson-%ED%99%9C%EC%9A%A9

 

c# 데이터 저장 <Newtonsoft.Json 활용>

0. 저장할 클래스 설정 class Player { // public으로 선언해 줘야 데이터 저장 및 불러오기가 가능하다 public int Hp { get; set; } public int Attack { get; set; } public int Defense { get; set; } public Player(int hp, int attack, i

rimugiri.tistory.com

문제

Interface의 경우 데이터를 불러 올 상황에 JsonConvert 오류가 발생하게 된다.
따라서 이를 해결하기 위한 방안을 소개한다.

0. 데이터 저장 클래스

▶ 예제 클래스 및 인터페이스 선언
class Player
{
    public string Name { get; set; }
    public float Hp { get; set; }
    public float Attack { get; set; }
    public int Defense { get; set; }

    public List<IItem> itemList = new List<IItem>();
    public Player(string _name, float _hp, float _attack, int _defense)
    {
        Name = _name;
        Hp = _hp;
        Attack = _attack;
        Defense = _defense;
    }
}

interface IItem
{
    public string Name { get; set; }
    public string Description { get; set; }
    ItemType Type { get; }
    public string ItemInfo();
}

class Weapon : IItem
{
    public string Name { get; set; }
    public string Description { get; set; }
    // 이 변수를 통해 어떤 클래스 인지 확인
    public ItemType Type => ItemType.Weapon;
    public int Attack { get; set; }
    public Weapon(string _name, string _description, int _attack)
    {
        Name = _name;
        Description = _description;
        Attack = _attack;
    }
    public string ItemInfo()
    {
        return $"이름 : {Name} | 공격력 {Attack} | {Description}";
    }
}

class Shield : IItem
{
    public string Name { get; set; }
    public string Description { get; set; }
    // 이 변수를 통해 어떤 클래스 인지 확인
    public ItemType Type => ItemType.Shield;
    public int Defense { get; set; }
    public Shield(string _name, string _description, int _defense)
    {
        Name = _name;
        Description = _description;
        Defense = _defense;
    }
    public string ItemInfo()
    {
        return $"이름 : {Name} | 방어력 {Defense} | {Description}";
    }
}
enum ItemType
{
    Weapon,
    Shield
}

1. 사용자 정의 JsonConverter 정의

▶ 클릭 하세요
public class ItemJsonConverter : JsonConverter
{
    // IItem 타입인 경우에만 이 Converter 적용
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(IItem);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);

        // class에서 정의한 ItemType를 통한 class 종류 확인
        ItemType itemType = item["Type"].ToObject<ItemType>();

        // IItem을 상속받은 class 타입별 변환
        switch (itemType)
        {
            case ItemType.Weapon:
                return item.ToObject<Weapon>();
            case ItemType.Shield:
                return item.ToObject<Shield>();
        }
        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

2. interface list 포함 데이터 저장

▶ 클릭 하세요
void SaveData(Player player)
{
    string FilePath = ""; // 파일경로설정
    try
    {
        string json = JsonConvert.SerializeObject(player, Formatting.Indented, new JsonSerializerSettings
        {
            // interface를 담고있는 리스트를 저장하기 위한 사용자 정의 Converter
            Converters = new List<JsonConverter> { new ItemJsonConverter() }
        });
        File.WriteAllText(FilePath, json);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error saving data: {ex.Message}");
    }
}

3. interface 포함 데이터 불러오기

▶ 클릭 하세요
Player LoadData()
{
    string FilePath = ""; // 파일경로설정
    try
    {
        if (File.Exists(FilePath))
        {
            string json = File.ReadAllText(FilePath);
            return JsonConvert.DeserializeObject<Player>(json, new JsonSerializerSettings
            {
                Converters = new List<JsonConverter> { new ItemJsonConverter() }
            });
        }
        else
        {
            return new Player("BraveSoldier", 200, 20, 5);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Error Load data: {ex.Message}");
        return new Player("BraveSoldier", 200, 20, 5);
    }
}

4.예제

▶ 클릭 하세요
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace MyApp // Note: actual namespace depends on the project name.
{
    class Program
    {
        static void Main(string[] args)
        {
            // 데이터 저장 예제
            // Player player = new Player("BraveSoldier", 300, 30, 10);
            // player.itemList.Add(new Weapon("검", "검이다", 10));
            // player.itemList.Add(new Shield("방패", "방패이다", 10));
            // Console.WriteLine(player.itemList[0].ItemInfo());
            // Console.WriteLine(player.itemList[1].ItemInfo());
            // Console.WriteLine($"Hp : {player.Hp}\nAttack : {player.Attack}\nDefense : {player.Defense}");
            // SaveData(player);

            // 데이터 불러오기 예제
            Player player = LoadData();
            Console.WriteLine(player.itemList[0].ItemInfo());
            Console.WriteLine(player.itemList[1].ItemInfo());
            Console.WriteLine($"Hp : {player.Hp}\nAttack : {player.Attack}\nDefense : {player.Defense}");
        }
        static void SaveData(Player player)
        {
            string FilePath = "../../../PlayerData.json"; // 파일경로설정
            try
            {
                string json = JsonConvert.SerializeObject(player, Newtonsoft.Json.Formatting.Indented, new JsonSerializerSettings
                {
                    // interface를 담고있는 리스트를 저장하기 위한 사용자 정의 Converter
                    Converters = new List<JsonConverter> { new ItemJsonConverter() }
                });
                File.WriteAllText(FilePath, json);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error saving data: {ex.Message}");
            }
        }
        static Player LoadData()
        {
            string FilePath = "../../../PlayerData.json"; // 파일경로설정
            try
            {
                if (File.Exists(FilePath))
                {
                    string json = File.ReadAllText(FilePath);
                    return JsonConvert.DeserializeObject<Player>(json, new JsonSerializerSettings
                    {
                        Converters = new List<JsonConverter> { new ItemJsonConverter() }
                    });
                }
                else
                {
                    return new Player("BraveSoldier", 200, 20, 5);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error Load data: {ex.Message}");
                return new Player("BraveSoldier", 200, 20, 5);
            }
        }
    }
    class Player
    {
        public string Name { get; set; }
        public float Hp { get; set; }
        public float Attack { get; set; }
        public int Defense { get; set; }

        public List<IItem> itemList = new List<IItem>();
        public Player(string _name, float _hp, float _attack, int _defense)
        {
            Name = _name;
            Hp = _hp;
            Attack = _attack;
            Defense = _defense;
        }
    }

    interface IItem
    {
        public string Name { get; set; }
        public string Description { get; set; }
        ItemType Type { get; }
        public string ItemInfo();
    }

    class Weapon : IItem
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public ItemType Type => ItemType.Weapon;
        public int Attack { get; set; }
        public Weapon(string _name, string _description, int _attack)
        {
            Name = _name;
            Description = _description;
            Attack = _attack;
        }
        public string ItemInfo()
        {
            return $"이름 : {Name} | 공격력 {Attack} | {Description}";
        }
    }

    class Shield : IItem
    {
        public string Name { get; set; }
        public string Description { get; set; }
        public ItemType Type => ItemType.Shield;
        public int Defense { get; set; }
        public Shield(string _name, string _description, int _defense)
        {
            Name = _name;
            Description = _description;
            Defense = _defense;
        }
        public string ItemInfo()
        {
            return $"이름 : {Name} | 방어력 {Defense} | {Description}";
        }
    }
    enum ItemType
    {
        Weapon,
        Shield
    }
    public class ItemJsonConverter : JsonConverter
    {
        // IItem 타입인 경우에만 이 Converter 적용
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(IItem);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JObject item = JObject.Load(reader);

            // class에서 정의한 ItemType를 통한 class 종류 확인
            ItemType itemType = item["Type"].ToObject<ItemType>();

            // IItem을 상속받은 class 타입별 변환
            switch (itemType)
            {
                case ItemType.Weapon:
                    return item.ToObject<Weapon>();
                case ItemType.Shield:
                    return item.ToObject<Shield>();
            }
            return null;
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            serializer.Serialize(writer, value);
        }
    }
}
728x90