Сериализация Generic Dictionary

Общие вопросы о Unity3D

Сериализация Generic Dictionary

Сообщение IDoNotExist 08 авг 2014, 21:07

Собственно вопрос: какие есть варианты сериализации Dictionary?
Вот пример из документации, тут предлагается список ключей и значений записывать в два отдельных списка. Но что то мне такой вариант не очень нравится, не очень хотелось бы для каждого словаря создавать дополнительно два списка, будет явно переизбыток данных, понятно что списки можно очищать после десериализации, но всеравно не очень красивый вариант.
Когда мы объявляем просто сериализуемую переменную или массив, [unity 3D] как то же обходится без дополнительных костылей, сериализует и сохраняет данные где то у себя в недрах, могу ли я таким же образом сохранить данные в своем формате при сериализации компонента и вытащить их при его десериализации, без лишнего "жира"?
Аватара пользователя
IDoNotExist
Адепт
 
Сообщения: 1432
Зарегистрирован: 23 мар 2011, 09:18
Skype: iamnoexist

Re: Сериализация Generic Dictionary

Сообщение Salamandr 08 авг 2014, 21:23

убирайте ключи и сохраняйте только значения
(не мой пример)
Синтаксис:
Используется csharp
class herb_pos
{
var x:float;
var y:float;
var z:float;
}
 
var herbs : GameObject[];
var load_herbs=new Array();
var herb_proto : GameObject;
 
function Write()
{
var w: herb_pos  = new herb_pos();
herbs = GameObject.FindGameObjectsWithTag("herb");
var fs: FileStream  = new FileStream("herb.sbin", FileMode.Create);
var bf: BinaryFormatter  = new BinaryFormatter();
var herb_count:int=herbs.length;
bf.Serialize(fs, herb_count);
    for (var herb : GameObject in herbs) {
    w.x=herb.transform.position.x;
    w.y=herb.transform.position.y;
    w.z=herb.transform.position.z;
    bf.Serialize(fs, w);
    }
fs.Close();
}
 
function Read()
{  
var vf: IFormatter  = new BinaryFormatter();
var fs = new FileStream("herb.sbin", FileMode.Open, FileAccess.Read);
var herb_count:int=vf.Deserialize(fs);
    for (var i=0;i<herb_count;i++) {
    var w1: herb_pos= vf.Deserialize(fs);
    load_herbs.Add(Instantiate (herb_proto, Vector3(w1.x,w1.y,w1.z), herb_proto.transform.rotation));
    load_herbs[herb_count].name="herb"+herb_count;
    }
fs.Close();
}
возможно всё, вопрос лишь в том, есть ли у тебя на это время
группа вк: _ttp://vk.com/sector5661
Аватара пользователя
Salamandr
UNITрон
 
Сообщения: 228
Зарегистрирован: 30 июл 2014, 13:04
Откуда: Астрахань, Каменск-Уральский
Skype: zzzubec
  • ICQ

Re: Сериализация Generic Dictionary

Сообщение BenjaminMoore 08 авг 2014, 22:00

IDoNotExist писал(а):Собственно вопрос: какие есть варианты сериализации Dictionary?

Если коротко - в таком виде никак.
Дженерик классы не сериализуются, за исключением специального костыля для листа
Всё
просто объявить у себя класс
Синтаксис:
Используется csharp
[System.Serializable]
class Foo<T>
{
   public T Value;
}
 


и объявите как поле
Синтаксис:
Используется csharp
class TestGeneric : MonoBehaviour
{
    public Foo<int> Test;
}
 


и Вы ничего не увидите в иснпекторе :)

хотя если сделаете так
Синтаксис:
Используется csharp
class FooInt : Foo<int>
 

и объявите его, он сериализуется
с дикшинари нужно проделать то же самое
благо он не sealed
наследуйте его и он должен сериализоваться :)
My hands are hard. My mind is core.
ring0x0000 c0x0063 | write code in rust right now
Аватара пользователя
BenjaminMoore
UNITрон
 
Сообщения: 338
Зарегистрирован: 03 янв 2013, 18:07
Skype: benjminmoore

Re: Сериализация Generic Dictionary

Сообщение IDoNotExist 08 авг 2014, 22:37

Salamandr писал(а):убирайте ключи и сохраняйте только значения

Куда их убирать? В чем смысл? Если бы мне нужны были только значения я взял бы List, речь идет именно о Dictionary.

BenjaminMoore писал(а):
IDoNotExist писал(а):Собственно вопрос: какие есть варианты сериализации Dictionary?

Если коротко - в таком виде никак.

Я понимаю что никак, вопрос был о том как кастомизировать сериализацию.

BenjaminMoore писал(а):Дженерик классы не сериализуются, за исключением специального костыля для листа
Всё
просто объявить у себя класс
Синтаксис:
Используется csharp
[System.Serializable]
class Foo<T>
{
   public T Value;
}
 


и объявите как поле
Синтаксис:
Используется csharp
class TestGeneric : MonoBehaviour
{
    public Foo<int> Test;
}
 


и Вы ничего не увидите в иснпекторе :)

хотя если сделаете так
Синтаксис:
Используется csharp
class FooInt : Foo<int>
 

и объявите его, он сериализуется
с дикшинари нужно проделать то же самое
благо он не sealed
наследуйте его и он должен сериализоваться :)

Эммм, что именно то наследовать? Такой фокус "class FooDict : Dictionary<string,int>" с Dictionary не прокатит, [unity 3D] в принципе не знает как сериализовывать Dictionary.
Аватара пользователя
IDoNotExist
Адепт
 
Сообщения: 1432
Зарегистрирован: 23 мар 2011, 09:18
Skype: iamnoexist

Re: Сериализация Generic Dictionary

Сообщение BlackMamba 08 авг 2014, 23:07

имхо, самый простой способ сериализации/десериализации это использование BinaryFormatter - сериализует любой объект с атрибутом Serializable, если тот состоит из, опять же, сериализуемых полей, успешно работает с массивами таких типов. могу подкинуть пищу для размышлений "приблизительным кодом" - не факт, что сразу заработает.

Синтаксис:
Используется csharp
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;

[Serializable]
public class SerializableDictionary<K,V>
{
        [Serializable]
        private struct Pair
        {
                public K Key { get; set; }
                public V Value { get; set; }
        }

        private Pair[] _pairs;

        private SerializableDictionary() { }

        public static explicit operator Dictionary<K, V>(SerializableDictionary<K, V> from)
        {
                if (from == null) throw new ArgumentNullException("SerializableDictionary.explicit from");
                Dictionary<K, V> res = new Dictionary<K, V>();
                from._pairs.Select(p => res[p.Key] = p.Value).ToArray();
                return res;
        }
        public static explicit operator SerializableDictionary<K, V>(Dictionary<K, V> from)
        {
                if (from == null) throw new ArgumentNullException("SerializableDictionary.explicit from");
                SerializableDictionary<K, V> sd = new SerializableDictionary<K, V>()
                {
                        _pairs = from.Select(p => new Pair(){Key = p.Key, Value = p.Value}).ToArray()
                };
                return sd;
        }
       

        public void example()
        {
                Dictionary<int, string> source = new Dictionary<int, string>();
                byte[] dest = ((SerializableDictionary<int, string>)source).Serialize();
                source = (Dictionary<int, string>)(dest.Deserialize<int, string>());
        }
}
public static class Helpers
{
        public static byte[] Serialize<K,V>(this SerializableDictionary<K, V> from)
        {
                if (from == null) throw new ArgumentNullException("SerializableDictionary.Serialize from");
                byte[] res;
                using (MemoryStream ms = new MemoryStream())
                {
                        BinaryFormatter f = new BinaryFormatter();
                        f.Serialize(ms, from);

                        res = ms.ToArray();
                        ms.Close();

                }
                return res;
        }
        public static SerializableDictionary<K, V> Deserialize<K,V>(this byte[] from)
        {
                if (from == null) throw new ArgumentNullException("SerializableDictionary.Deserialize from");
                SerializableDictionary<K, V> to;
                using (MemoryStream ms = new MemoryStream(from))
                {
                        BinaryFormatter f = new BinaryFormatter();
                        to = (SerializableDictionary<K, V>)f.Deserialize(ms);
                        ms.Close();
                }
                return to;
        }
}


из опыта: K,V - встроенные типы и массивы типов, кроме uint, ushort и т.д., а также сериализуемые подобным образом иные типы и их массивы, кроме перечислений
mail: _gdeMoiGusi@gmail.com
skype: Ellseworth
Аватара пользователя
BlackMamba
UNITрон
 
Сообщения: 305
Зарегистрирован: 06 янв 2011, 16:16
Откуда: Москва

Re: Сериализация Generic Dictionary

Сообщение Neodrop 09 авг 2014, 09:48

Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Сериализация Generic Dictionary

Сообщение Neodrop 09 авг 2014, 15:56

Теперь есть интерфейс ISerializationCallbackReceiver реализующий вызовы методов перед сериализацией и после десериализации.
Ловим перед их сохраняем/загружаем наш Dictionary теми инструментами, что нам более нравятся.
Добавить neodrop в Skype
Изображение
"Спасибо!" нашему порталу, вы сможете сказать ЗДЕСЬ.
Если проблема не решается честно, нужно её обмануть! || Per stupiditas at Astra!
Страх порождает слабость. Бесстрашных поражают пули.
Протратившись на блядях байтах, на битах не экономят.
Аватара пользователя
Neodrop
Админ
 
Сообщения: 8480
Зарегистрирован: 08 окт 2008, 15:42
Откуда: Питер
Skype: neodrop
  • Сайт

Re: Сериализация Generic Dictionary

Сообщение BenjaminMoore 09 авг 2014, 18:10

Эммм, что именно то наследовать? Такой фокус "class FooDict : Dictionary<string,int>" с Dictionary не прокатит, [unity 3D] в принципе не знает как сериализовывать Dictionary.

да, там приватные поля с дженерик коллекшинами, потому такое не поможет. неодроп вроде хорошее решение подсказал, но оно локальное
My hands are hard. My mind is core.
ring0x0000 c0x0063 | write code in rust right now
Аватара пользователя
BenjaminMoore
UNITрон
 
Сообщения: 338
Зарегистрирован: 03 янв 2013, 18:07
Skype: benjminmoore

Re: Сериализация Generic Dictionary

Сообщение Neyl 09 авг 2014, 21:53

Можно так.
Базовый generic класс.
Синтаксис:
Используется csharp
using System;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class SerializedDictionary<TKey, TValue> : Dictionary<TKey, TValue>, ISerializationCallbackReceiver
{
    [SerializeField]
    private List<TKey> m_keys = new List<TKey>();
    [SerializeField]
    private List<TValue> m_values = new List<TValue>();


    public void OnBeforeSerialize()
    {
        m_keys.Clear();
        m_values.Clear();
        foreach (KeyValuePair<TKey, TValue> kvp in this)
        {
            m_keys.Add(kvp.Key);
            m_values.Add(kvp.Value);
        }
    }

    public void OnAfterDeserialize()
    {
        Clear();
        for (int i = 0; i != Math.Min(m_keys.Count, m_values.Count); i++)
        {
            Add(m_keys[i], m_values[i]);
        }
    }
}

От него наследовать уже нужные разновидности словарей, например
Синтаксис:
Используется csharp
[Serializable]
public class StringVector3Dictionary : SerializedDictionary<string, Vector3>
{
}

Пример использования
Синтаксис:
Используется csharp
using System;
using System.Globalization;
using UnityEngine;

public class SerializedDictionaryTester : MonoBehaviour
{
    public StringVector3Dictionary demoDictionary = new StringVector3Dictionary();


    void Start()
    {
        Debug.Log(demoDictionary.Count);
    }

    [ContextMenu("AddKeyValuePair")]
    void AddKeyValuePair()
    {
        demoDictionary.Add(DateTime.Now.ToString(CultureInfo.InvariantCulture), Vector3.up);
    }
}
 

Заполнять Dictionary придется через кастомный редактор.
Аватара пользователя
Neyl
UNIверсал
 
Сообщения: 404
Зарегистрирован: 20 июн 2010, 10:17

Re: Сериализация Generic Dictionary

Сообщение IDoNotExist 12 авг 2014, 09:46

Именно про использование интерфейса ISerializationCallbackReceiver я и говорил в первом сообщении, меня интересовало как избежать использования двух списков и сохранять напрямую в какой либо ассет, в общем пока что ничего хорошего из этого не получилось, во - первых метод OnBeforeSerialize() вызывается при каждом OnInspectorUpdate() когда объект выделен, это значит что надо постоянно проверять актуальные ли данные записаны в файл, чтобы постоянно не писать туда, во вторых в документации написано, OnBeforeSerialize() и OnAfterDeserialize() могут быть вызваны не в основном потоке, тоже не понятно что при этом произойдет, можно конечно залочить основные объекты, но что то не охота экспериментировать, в общем пока что использую сериализацию словаря в два списка, так как это самый простой способ.
Аватара пользователя
IDoNotExist
Адепт
 
Сообщения: 1432
Зарегистрирован: 23 мар 2011, 09:18
Skype: iamnoexist

Re: Сериализация Generic Dictionary

Сообщение smartxp 22 янв 2017, 13:51

Вижу тема старая, но плодить одинаковые не хочется.
Сделал так, как описал Neyl - после ребилда кода, значения слетают, не срабатывает OnBeforeSerialize() и OnAfterDeserialize(), надо ли что-то дополнительно прописывать, что бы эти функции срабатывали.
Обычные типы данных (int, string - вне Dictionary) сохраняются. ScriptableObject не использую, может обязательно его использовать в данном случае?

В конце OnInspectorGUI прописано:
Синтаксис:
Используется csharp
 
if (GUI.changed)
{
     EditorUtility.SetDirty(this.target);
     serializedObject.ApplyModifiedProperties();
}
 


Облазил весь интернет, но так проблему и не решил. Версия unity 5.5.0f3.
smartxp
UNец
 
Сообщения: 28
Зарегистрирован: 30 окт 2016, 12:29

Re: Сериализация Generic Dictionary

Сообщение IDoNotExist 22 янв 2017, 14:21

smartxp писал(а):Вижу тема старая, но плодить одинаковые не хочется.
Сделал так, как описал Neyl - после ребилда кода, значения слетают, не срабатывает OnBeforeSerialize() и OnAfterDeserialize(), надо ли что-то дополнительно прописывать, что бы эти функции срабатывали.
Обычные типы данных (int, string - вне Dictionary) сохраняются. ScriptableObject не использую, может обязательно его использовать в данном случае?

В конце OnInspectorGUI прописано:
Синтаксис:
Используется csharp
 
if (GUI.changed)
{
     EditorUtility.SetDirty(this.target);
     serializedObject.ApplyModifiedProperties();
}
 


Облазил весь интернет, но так проблему и не решил. Версия unity 5.5.0f3.


Так попробуйте:
Синтаксис:
Используется csharp
        Undo.RecordObject(target, GetType().Name);

        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
        }
 
Аватара пользователя
IDoNotExist
Адепт
 
Сообщения: 1432
Зарегистрирован: 23 мар 2011, 09:18
Skype: iamnoexist

Re: Сериализация Generic Dictionary

Сообщение seaman 22 янв 2017, 14:23

ISerializationCallbackReceiver не забыли?
seaman
Адепт
 
Сообщения: 8352
Зарегистрирован: 24 янв 2011, 12:32
Откуда: Самара

Re: Сериализация Generic Dictionary

Сообщение smartxp 22 янв 2017, 15:21

Undo.RecordObject(target, GetType().Name);

if (GUI.changed)
{
EditorUtility.SetDirty(target);
}

Не помогло

ISerializationCallbackReceiver - нет не забыл.
smartxp
UNец
 
Сообщения: 28
Зарегистрирован: 30 окт 2016, 12:29

Re: Сериализация Generic Dictionary

Сообщение IDoNotExist 22 янв 2017, 19:07

smartxp писал(а):Не помогло

ISerializationCallbackReceiver - нет не забыл.

Тогда показывайте полный код скрипта и инспектора для него.
Аватара пользователя
IDoNotExist
Адепт
 
Сообщения: 1432
Зарегистрирован: 23 мар 2011, 09:18
Skype: iamnoexist


Вернуться в Общие вопросы

Кто сейчас на конференции

Сейчас этот форум просматривают: GoGo.Ru [Bot] и гости: 10