18 февраля 2014 г.

Дерево строк и его (де)сериализация

Возникла потребность в сохранении сюжетных переменных, не определяя их на уровне кода движка. Логично было запихать все в Dictionary<string, string>, но родная NET-сериализация порождала много лишней информации. Пришлось написать свой вариант, добавив заодно возможность группировки данных по «папкам».


Задача состояла в том, чтобы навигация по дереву была такой же удобной как с XPath, а сериализованные данные занимали места даже меньше чем JSON. Получилось то, что получилось.

Написано на C# для Unity. Исходник можно скачать тут: StringTree.cs. Код далек от идеала, но в рамках испытаний показал себя достаточно быстрым и безотказным. Хотя алгоритм распаковки я, возможно, перепишу еще не раз.

Принцип работы


Допустим, мы имеем текстовый файл.
cabin {
    kitchen {
        fridge {
            // Количество мяса в холодильнике
            meat: 3;
        }
    }
}
// Данные о том, в какой комнате находится игрок
currentRoom: "Cabin Kitchen";
inventory {
    pistol: 1;
    ammo: 10;
}
Передав его конструктору, получаем готовое дерево. Теперь любая переменная доступна нам через индексатор, а метод Serialize позволит сохранить все данные в одну непрерывную строку.
StringTree stringTree = new StringTree(input);
string ammo = stringTree["inventory/ammo"];
stringTree["cabin/kitchen/fridge/meat"] = "1";
string output = stringTree.Serialize();

// ammo: 10
/* output: cabin/kitchen/fridge/meat:1;inventory{pistol:1;ammo:10}currentRoom:"Cabin Kitchen" */
Вот и все, это его основные функции.

Помимо них класс имеет пару методов, присущих Dictionary, такие как Count и Clear, а также кучу вспомогательных Get'ов. Их работа описана в комментариях к коду.

Чтобы использовать класс вне Unity, достаточно удалить «using UnityEngine» и все «Debug.LogError» (или заменить их на подходящие вам аналоги).

Если обнаружите какие-либо недочеты, пожалуйста, сообщите.


P.S. Имеется также готовое решение для подобного хранения и упаковки байтовых массивов, но пока не знаю, куда его можно применить. Для числовых и строковых данных объем записи практически не уменьшается, но ее становится невозможно прочитать глазами.

3 комментария:

  1. А не проще использовать формат json?

    ОтветитьУдалить
    Ответы
    1. По сути это и есть почти JSON, только без лишних кавычек и с возможностью присвоения значений несуществующим ранее переменным. Например, для описания предметов в инвентаре меня не устроил бы массив объектов вроде [ { "name" : "ammo", "count" : 10 } ] или двух параллельных массивов { "names" : [ "ammo" ], "counts" : [ 10 ] }. Но моя версия и не претендует на превосходство. :) Просто я не нашел более удобного способа сохранить состояние мира, структура которого не определена заранее. Хотя они, наверняка, есть. :)

      Удалить
    2. Дописал в тексте, почему не.

      Удалить