Страницы

Поиск по вопросам

Показаны сообщения с ярлыком сериализация. Показать все сообщения
Показаны сообщения с ярлыком сериализация. Показать все сообщения

суббота, 11 апреля 2020 г.

Наследование и сериализация

#c_sharp #сериализация #jsonnet

                    
Имеется базовый класс AudioObject, от которого наследуются два класса: Audio и AudioUser.
Также имеем класс AudioList, в котором есть поле List. В этом поле может
храниться список объектов смешанных типов: Audio и AudioUser.

public class AudioObject
{
}

public class Audio : AudioObject
{
    public uint id { get; set; }
    public string title { get; set; }
}

public class AudioUser : AudioObject
{
    public string id { get; set; }
    public string name { get; set; }
}

public class AudioList
{
    public int count { get; set; }
    public List list { get; set; }
}


Попытаемся сериализовать и десириализовать объкт AudioList:

AudioList list = new AudioList();
list.count = 1;
list.list = new List { new Audio { id = 1, title = "Test"} };

string serialized = JsonConvert.SerializeObject(list);

AudioList newList = JsonConvert.DeserializeObject(serialized);
Audio audio = newList.list[0] as Audio;


Сериализация происходит успешно, но вот десериализация возвращает нулевые объекты
из списка List.

В чем моя ошибка?
    


Ответы

Ответ 1



You are getting null for audio because items in deserialized list are instances of base AudioObject class - according to List<> declaration. Add type information to [de]serialization: AudioList list = new AudioList(); list.count = 1; list.list = new List { new Audio { id = 1, title = "Test"} }; JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All }; string serialized = JsonConvert.SerializeObject(list, settings); AudioList newList = JsonConvert.DeserializeObject(serialized, settings); Audio audio = newList.list[0] as Audio;

Ответ 2



Используйте настройку TypeNameHandling, чтобы информация о типах сохранялась в JSON: JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto // или All для того, чтобы десериализовать не только объекты дочерних типов, // но и коллекции разных типов (IList/IEnumerable) }; string serialized = JsonConvert.SerializeObject(list, settings); AudioList newList = JsonConvert.DeserializeObject(serialized, settings); Audio audio = newList.list[0] as Audio; // != null

вторник, 31 марта 2020 г.

Теряется ссылка на один и тот же объект при сериализации-десериализации через DataContract

#c_sharp #net #сериализация #десериализация


Пытаюсь сериализовать сложный объект через DataContract. Объект имеет список подобъектов,
которые имеют в свою очередь свой список (назовем - листья дерева). К этим листьям,
например, первого элемента списка основного объекта, можно добраться их других элементов
списка (ссылка одна и та же). Получается довольно сложный граф, в котором много связей.

При десериализации у каждого элемента списка - свои листья, т.е. ссылки теряются,
если в объекте источнике - это один и тот же лист, то после десериализации - просто копия.

Примерно так

    A                A
   / \              / \
  B   C    ====>   B   C
   \ /             |   |
    D              D   D[копия]


(это очень упрощенная схема)

Пробовал для всех классов прописать DataContract(IsReference = true), так, на всякий
случай - не помогло. 

Есть ли более "быстрый" способ корректной десериализации, кроме как "ручное" выстраивание
структуры повторяющей объект-источник?

Спасибо.
    


Ответы

Ответ 1



При создании сериализатора задайте ему настройку PreserveObjectReferences = true. var settings = new DataContractSerializerSettings { PreserveObjectReferences = true }; var dcs = new DataContractSerializer(typeof(SomeType), settings); Также можно использовать NetDataContractSerializer.

среда, 26 февраля 2020 г.

Как серелизовать произвольный объект в строку?

#python #python_3x #сериализация


У меня есть произвольный объект, например упрощенно для класса:

class Attribute:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def calc():
        return self.x + self.y


создаю объект:

attr = Attribute(1, 1)


Я хочу сохранить этот объект(attr) в строку и потом при необходимости его оттуда
загрузить. Я пытался использовать модуль pickle, но не осилил.

import pickle
pickled = pickle.dumps(attr)
spickled = str(pickled)

unpickled = pickle.loads(spickled)


Привело к ошибке:

TypeError: a bytes-like object is required, not 'str'


str(pickled) потому что на самом деле я должен сохранить свой объект в json как строку
и потом считать оттуда при другом запуске.

Собственно вопрос, как мне сохранить мой объект в json и потом его прочитать? Человекочитаемость
не обязательно.
    


Ответы

Ответ 1



import pickle import base64 pickled = pickle.dumps(attr) spickled = base64.standard_b64encode(pickled) unpickled = pickle.loads(base64.standard_b64decode(spickled)) print(unpickled, unpickled.x, unpickled.y)

Ответ 2



Если писать энкодер не вариант, значит, можно использовать менее элегантный способ: import ast import json import pickle class Attribute: def __init__(self, x, y): self.x = x self.y = y def calc(): return self.x + self.y def __eq__(self, oth): return self.__dict__ == oth.__dict__ obj = Attribute(1, 1) # --- dump object_bytes = pickle.dumps(obj) object_bytes_str = str(object_bytes) object_json_dump = json.dumps(object_bytes_str) # --- dump # --- load object_bytes_str = json.loads(object_json_dump) object_bytes = ast.literal_eval(object_bytes_str) obj_from_dump = pickle.loads(object_bytes) # --- load print(obj == obj_from_dump) Тут основная решаемая задача - получение bytes из строки с помощью ast.literal_eval. Пока писал, появился ответ от Xander, который куда более предпочтительнее.

пятница, 14 февраля 2020 г.

XML-сериализация по особым правилам

#c_sharp #xml #сериализация


Имеется электронный документооборот. Обмен данными выполняется с помощью XML следующей
структуры:


    
    
    
    
    
    
    
    
        
        
        
        
        
        
        
            
            
        
    
    
        
        
        
        
        
        
    



Проблема на лицо - требуется куча классов примерно такой структуры:

[XmlRoot(ElementName="tag1")]
public class Tag1
{
    [XmlAttribute(AttributeName="value")]
    public int Value { get; set; }
}


и потом:

[XmlRoot(ElementName="document")]
public class Document
{
    [XmlElement(ElementName="tag1")]
    public Tag1 Tag1 { get; set; }
    [XmlElement(ElementName="tag2")]
    public Tag2 Tag2 { get; set; }
    [XmlElement(ElementName="tag3")]
    public Tag3 Tag3 { get; set; }
    [XmlElement(ElementName="tag4")]
    public Tag4 Tag4 { get; set; }
    [XmlElement(ElementName="tag5")]
    public Tag5 Tag5 { get; set; }
    [XmlElement(ElementName="tag6")]
    public Tag6 Tag6 { get; set; }
    [XmlElement(ElementName="tag7")]
    public Tag7 Tag7 { get; set; }
    [XmlElement(ElementName="outerTag1")]
    public OuterTag1 OuterTag1 { get; set; }
    [XmlElement(ElementName="outerTag2")]
    public OuterTag2 OuterTag2 { get; set; }
}


Хотелось бы вместо этого написать класс со свойствами простых типов:

[XmlRoot(ElementName="document")]
public class Document
{
    [...(ElementName="tag1")]
    public int Tag1 { get; set; }
    [...(ElementName="tag2")]
    public string Tag2 { get; set; }
    [...(ElementName="tag3")]
    public DateTime Tag3 { get; set; }
    [...(ElementName="tag4")]
    public int Tag4 { get; set; }
    [...(ElementName="tag5")]
    public DateTime Tag5 { get; set; }
    [...(ElementName="tag6")]
    public string Tag6 { get; set; }
    [...(ElementName="tag7")]
    public decimal Tag7 { get; set; }
    [XmlElement(ElementName="outerTag1")]
    public OuterTag1 OuterTag1 { get; set; }
    [XmlElement(ElementName="outerTag2")]
    public OuterTag2 OuterTag2 { get; set; }
}


и не плодить кучу мелких классов типа Tag1, Tag2, ...

Можно ли как-то это сделать? В идеале хотелось бы сделать кастомный атрибут MyXmlElement
и использовать его вместо XmlElement, но как научить XmlSerializer понимать его и генерировать
соответствующую разметку? Или может есть какой-то другой способ?
    


Ответы

Ответ 1



А давайте сделаем кодогенерацию? Т4 прекрасно подходит. Мы создадим два класса: один со вложенными классами для сериализации, и другой плоский, с которым легко и удобно работать. И конвертирующие функции. Генерировать будем на основе вот такого XML-документа (я положил его в проект под названием DocumentProto.xml). Кладём в проект новый файл типа T4 через Add New Item → Text Template (не Runtime TextTemplate!). Я назвал его Document.tt. В первой строке меняем hostspecific="false" на true. Добавляем нужные сборки: <#@ assembly name="System.Xml" #> <#@ assembly name="System.Xml.Linq" #> и <#@ import namespace="System.Xml.Linq" #> меняем output extension на ".cs". Дальше дело техники: нам нужно распарсить XML. Открываем документ, читаем его в память: <# var xmlpath = Host.ResolvePath("DocumentProto.xml"); XDocument xd = XDocument.Load(xmlpath); #> Создаём шаблон файла: // Generated code! Do not edit! using System; using System.Xml.Serialization; namespace CodegenTest { // будем добавлять тут } Теперь, генерация набора классов для сериализации. Пишем (предварительно отладив это на тестовом приложении командной строки) в конце tt-файла: <#+ void GenerateNestedClasses(XElement element) { var childClasses = new Queue(); string className = element.Name.LocalName; string capitalizedClassName = char.ToUpper(className[0]) + className.Substring(1); WriteLine($"[XmlRoot(ElementName=\"{className}\")]"); WriteLine($"class {capitalizedClassName}"); WriteLine("{"); foreach (var sub in element.Elements()) { string type; string name = sub.Name.LocalName; string capitalizedName = char.ToUpper(name[0]) + name.Substring(1); if (!sub.HasAttributes) // nested class { type = capitalizedName; childClasses.Enqueue(sub); } else { type = (string)sub.Attribute("type"); } WriteLine($" [XmlElement(ElementName=\"{name}\")]"); WriteLine($" public {type} {capitalizedName} {{ get; set; }}"); } WriteLine("}"); WriteLine(""); foreach (var child in childClasses) GenerateNestedClasses(child); } #> (Внутри тегов <#+ #> располагаются дополнительные методы для генерации.) Пользуемся: namespace Serialization { <# PushIndent(" "); GenerateNestedClasses(xd.Root); ClearIndent(); #> } Получаем в Document.cs: namespace Serialization { [XmlRoot(ElementName="document")] class Document { [XmlElement(ElementName="tag1")] public int Tag1 { get; set; } [XmlElement(ElementName="tag2")] public string Tag2 { get; set; } [XmlElement(ElementName="tag3")] public DateTime Tag3 { get; set; } [XmlElement(ElementName="tag4")] public int Tag4 { get; set; } [XmlElement(ElementName="tag5")] public DateTime Tag5 { get; set; } [XmlElement(ElementName="tag6")] public string Tag6 { get; set; } [XmlElement(ElementName="tag7")] public double Tag7 { get; set; } [XmlElement(ElementName="outerTag1")] public OuterTag1 OuterTag1 { get; set; } [XmlElement(ElementName="outerTag2")] public OuterTag2 OuterTag2 { get; set; } } [XmlRoot(ElementName="outerTag1")] class OuterTag1 { [XmlElement(ElementName="innerTag11")] public int InnerTag11 { get; set; } [XmlElement(ElementName="innerTag12")] public string InnerTag12 { get; set; } [XmlElement(ElementName="innerTag13")] public string InnerTag13 { get; set; } [XmlElement(ElementName="innerTag14")] public int InnerTag14 { get; set; } [XmlElement(ElementName="innerTag15")] public int InnerTag15 { get; set; } [XmlElement(ElementName="innerTag16")] public int InnerTag16 { get; set; } [XmlElement(ElementName="innerOuterTag11")] public InnerOuterTag11 InnerOuterTag11 { get; set; } } [XmlRoot(ElementName="innerOuterTag11")] class InnerOuterTag11 { [XmlElement(ElementName="innerInnerTag111")] public string InnerInnerTag111 { get; set; } [XmlElement(ElementName="innerInnerTag112")] public DateTime InnerInnerTag112 { get; set; } } [XmlRoot(ElementName="outerTag2")] class OuterTag2 { [XmlElement(ElementName="innerTag21")] public string InnerTag21 { get; set; } [XmlElement(ElementName="innerTag22")] public string InnerTag22 { get; set; } [XmlElement(ElementName="innerTag23")] public string InnerTag23 { get; set; } [XmlElement(ElementName="innerTag24")] public string InnerTag24 { get; set; } [XmlElement(ElementName="innerTag25")] public string InnerTag25 { get; set; } [XmlElement(ElementName="innerTag26")] public string InnerTag26 { get; set; } } } Добавляем ещё в конец файла генерацию свойств «плоского класса»: void GenerateFlatClassProps(XElement element) { foreach (var sub in element.Elements()) { if (!sub.HasAttributes) // nested GenerateFlatClassProps(sub); else { var type = (string)sub.Attribute("type"); string name = sub.Name.LocalName; string capitalizedName = char.ToUpper(name[0]) + name.Substring(1); WriteLine($"public {type} {capitalizedName} {{ get; set; }}"); } } } и метода чтения свойств в плоский класс: void GenerateFlatteningBody(XElement element, string path) { string name = element.Name.LocalName; string capitalizedName = char.ToUpper(name[0]) + name.Substring(1); foreach (var sub in element.Elements()) { string subName = sub.Name.LocalName; string capitalizedSubName = char.ToUpper(subName[0]) + subName.Substring(1); if (!sub.HasAttributes) // nested GenerateFlatteningBody(sub, path + "." + capitalizedSubName); else WriteLine($"this.{capitalizedSubName} = that{path}.{capitalizedSubName};"); } } Пользуемся ими наверху: public class Document { <# PushIndent(" "); GenerateFlatClassProps(xd.Root); ClearIndent(); #> private void AssignFromSerialized(Serialization.Document that) { <# PushIndent(" "); GenerateFlatteningBody(xd.Root, ""); ClearIndent(); #> } internal static Document FromSerialized(Serialization.Document sdoc) { var doc = new Document(); doc.AssignFromSerialized(sdoc); return doc; } } Получаем готовый сгенерированный набор классов для использования: namespace CodegenTest { public class Document { public int Tag1 { get; set; } public string Tag2 { get; set; } public DateTime Tag3 { get; set; } public int Tag4 { get; set; } public DateTime Tag5 { get; set; } // ... private void AssignFromSerialized(Serialization.Document that) { this.Tag1 = that.Tag1; this.Tag2 = that.Tag2; // ... this.InnerTag11 = that.OuterTag1.InnerTag11; this.InnerTag12 = that.OuterTag1.InnerTag12; this.InnerTag13 = that.OuterTag1.InnerTag13; this.InnerTag14 = that.OuterTag1.InnerTag14; this.InnerTag15 = that.OuterTag1.InnerTag15; this.InnerTag16 = that.OuterTag1.InnerTag16; this.InnerInnerTag111 = that.OuterTag1.InnerOuterTag11.InnerInnerTag111; // ... } internal static Document FromSerialized(Serialization.Document sdoc) { var doc = new Document(); doc.AssignFromSerialized(sdoc); return doc; } } namespace Serialization { [XmlRoot(ElementName="document")] class Document { [XmlElement(ElementName="tag1")] public int Tag1 { get; set; } // ... [XmlElement(ElementName="outerTag1")] public OuterTag1 OuterTag1 { get; set; } [XmlElement(ElementName="outerTag2")] public OuterTag2 OuterTag2 { get; set; } } [XmlRoot(ElementName="outerTag1")] class OuterTag1 { [XmlElement(ElementName="innerTag11")] public int InnerTag11 { get; set; } [XmlElement(ElementName="innerTag12")] public string InnerTag12 { get; set; } // ... } // ... } } Обновление: исправил GenerateNestedClasses на такое: void GenerateNestedClasses(XElement element) { var childClasses = new Queue(); var leafClasses = new Queue(); string className = element.Name.LocalName; string capitalizedClassName = char.ToUpper(className[0]) + className.Substring(1); WriteLine($"[XmlRoot(ElementName=\"{className}\")]"); WriteLine($"class {capitalizedClassName}"); WriteLine("{"); foreach (var sub in element.Elements()) { string name = sub.Name.LocalName; string capitalizedName = char.ToUpper(name[0]) + name.Substring(1); if (!sub.HasAttributes) // nested class childClasses.Enqueue(sub); else leafClasses.Enqueue(sub); WriteLine($" [XmlElement(ElementName=\"{name}\")]"); WriteLine($" public {capitalizedName} {capitalizedName} {{ get; set; }}"); } WriteLine("}"); WriteLine(""); foreach (var leaf in leafClasses) GenerateLeafClass(leaf); foreach (var child in childClasses) GenerateNestedClasses(child); } void GenerateLeafClass(XElement element) { string className = element.Name.LocalName; string capitalizedClassName = char.ToUpper(className[0]) + className.Substring(1); string type = (string)element.Attribute("type"); WriteLine($"class {capitalizedClassName}"); WriteLine("{"); WriteLine($" [XmlAttribute]"); WriteLine($" public {type} Value {{ get; set; }}"); WriteLine("}"); WriteLine(""); } и в GenerateFlatteningBody последнюю строчку на WriteLine($"this.{capitalizedSubName} = that{path}.{capitalizedSubName}.Value;"); Получились промежуточные классы вида class Tag1 { [XmlAttribute] public int Value { get; set; } } На всякий случай, полный код tt-шаблона и сгенерированного результата: https://gist.github.com/vladd/7f25e0ceb625372bffdbf9b455452ae1

Ответ 2



Отказался от реализации интерфейса IXmlSerializable - решение получалось очень громоздким и не красивым, к тому же нужно учесть много всевозможных нюансов, которые учтены в штатной работе сериализатора. В итоге написал простой класс: public class Tag { [XmlAttribute(AttributeName = "value")] public T Value { get; set; } public override string ToString() => Value.ToString(); public static implicit operator Tag(T value) => new Tag { Value = value }; public static implicit operator T(Tag tag) => tag.Value; } Это позволило выбросить кучу мелких классов Tag1, Tag2 и т.д. Сам документ принял вид: [XmlRoot(ElementName="document")] public class Document { [XmlElement(ElementName="tag1")] public Tag Tag1 { get; set; } [XmlElement(ElementName="tag2")] public Tag Tag2 { get; set; } [XmlElement(ElementName="tag3")] public Tag Tag3 { get; set; } [XmlElement(ElementName="tag4")] public Tag Tag4 { get; set; } [XmlElement(ElementName="tag5")] public Tag Tag5 { get; set; } [XmlElement(ElementName="tag6")] public Tag Tag6 { get; set; } [XmlElement(ElementName="tag7")] public Tag Tag7 { get; set; } [XmlElement(ElementName="outerTag1")] public OuterTag1 OuterTag1 { get; set; } [XmlElement(ElementName="outerTag2")] public OuterTag2 OuterTag2 { get; set; } } Ну и благодаря операторам для неявного приведения типов документ создается так же просто: var doc = new Document { Tag1 = 1, Tag2 = "text", ... };

воскресенье, 9 февраля 2020 г.

Сериализация сложного JAVA объекта в JSON

#java #json #сериализация


Добрый день подскажите как сериализовать в JSON объект Files? Используя метод toJsonString
сериализуются только поле nameDirectory, а поле pool выводится в виде хеша?

import org.json.simple.JSONValue;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;

public class Files {

    public Map pool;

    public String nameDirectory;

    public String toJsonString(){
        JSONObject JSobj = new JSONObject(pool);
        JSobj.toJSONString();
    }
}

public class File {
    String PathFile;
    ArrayList Records;
    boolean PutInEnd;
}

public class Record {

    private String recordname;
    private String status;
    private int countExecution;
}

    


Ответы

Ответ 1



Для таких целей есть специальные библиотеки - Jackson и Gson. import java.io.File; import java.io.IOException; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap; import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.JsonMappingException; class Record { // Поле name и так находится в Record, // называть его recordname - это лишнее private String name; private String status; private int executionCount; public Record() {} public Record(String name, String status, int executionCount) { setName(name); setStatus(status); setExecutionCount(executionCount); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void setStatus(String status) { this.status = status; } public String getStatus() { return status; } public void setExecutionCount(int executionCount) { this.executionCount = executionCount; } public int getExecutionCount() { return executionCount; } } // Плохая идея использовать имена классов // входящих в стандартную библиотеку class SomeFile { private String path; // Имена полей должны начинаться со строчной буквы private List records; private boolean putInEnd; public SomeFile() { records = new ArrayList<>(); } public SomeFile(String path, boolean putInEnd) { this(); setPath(path); setPutInEnd(putInEnd); } public void setPath(String path) { this.path = path; } public String getPath() { return path; } public void setPutInEnd(boolean putInEnd) { this.putInEnd = putInEnd; } public void addRecord(Record record) { records.add(record); } public List getRecords() { return records; } } class SomeFiles { private String name; private Map pool; public SomeFiles() { pool = new HashMap<>(); } public SomeFiles(String name) { this(); setName(name); } public void setName(String name) { this.name = name; } public String getName() { return name; } public void putSomeFile(String name, SomeFile someFile) { pool.put(name, someFile); } public Map getPool() { return pool; } } public class App { public static void main(String[] args) { SomeFile f = new SomeFile("/root/secret.txt", false); f.addRecord(new Record("Some Record", "Some status", 42)); f.addRecord(new Record("Another Record", "Same status", 0)); SomeFiles sf = new SomeFiles("Some Files"); sf.putSomeFile("Some File", f); ObjectMapper mapper = new ObjectMapper(); // Для вывода с отступами mapper.enable(SerializationFeature.INDENT_OUTPUT); try { // Здесь происходит самая главная магия mapper.writeValue(new File("some_files.json"), sf); } catch(JsonGenerationException exc) { exc.printStackTrace(); } catch(JsonMappingException exc) { exc.printStackTrace(); } catch(IOException exc) { exc.printStackTrace(); } } } Получится такое: { "name" : "Some Files", "pool" : { "Some File" : { "path" : "/root/secret.txt", "records" : [ { "name" : "Some Record", "status" : "Some status", "executionCount" : 42 }, { "name" : "Another Record", "status" : "Same status", "executionCount" : 0 } ] } } }

Ответ 2



У вас 2 проблемы: Сериализация Map Сериализация File Если с первой более-менее все понятно - в рамках Gson решается приблизительно так: Map myMap; Type typeOfMap = new TypeToken>() { }.getType(); Gson gson = new GsonBuilder().create(); String json = gson.toJson(myMap, typeOfMap); Вторая проблема более концептуальная что-ли. Никто кроме вас не понимает, что вы имеете ввиду под сериализацией объекта типа File: или вы захотите просто сериализовать имя файла или имя файла включая полный путь или универсальный URI или же вообще содержимое файла. На этот вопрос кроме вас никто не ответит. Чтобы оформить свое отношение к сериализации объекта типа File вам нужно написать свой собственный сериалайзер - в терминах GSon это выглядит примерно так: public class MyFileAdapter implements JsonSerializer { @Override public JsonElement serialize(File src, Type typeOfSrc, JsonSerializationContext context) { //blah-blah } } } Далее надо известить Gson о том, что отныне объекты типа File будут обрабатываться вашим адаптером: GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(File.class, new MyFileAdapter());

пятница, 31 января 2020 г.

Сериализация в Python. Почему так много модулей?

#python #сериализация


Я мало знаком с сериализацией. Вот что я понял:
Есть пакеты, удобные для человека json, есть пакеты, удобные для хранения в Python
- pickle. Есть межязыковые пакеты - protobuf.
Ну три модуля должно бы хватить?
Когда я открыл PyPi с поисковым вопросом 'serialization' - я получил 4+ страницы
модулей. Например  protobuf имеет 3 модуля... 
Итак 2 вопроса.
1. Почему dill например, не совместили с pickle?
2. Можно ли обойтись 3-4 стандартами на все случаи жизни? Если нет - то какова причина
такого разнообразия модулей? 
    


Ответы

Ответ 1



По-моему нет смысла бороться с эволюцией и естественным отбором. Хорошие, удобные, эффективные, etc. модули вытеснят те, которые им проигрывают. Пример из моей рабочей жизни: около 2 лет назад я выбрал для себя, как мне тогда казалось, идеальный модуль - HDF5 (PyTables) для быстрой и удобной (де-)сериализации данных в Pandas. Несколько позже появилась новая библиотека - Feather Format (Apache Arrow), которая значительно быстрее HDF5, но в ней отсутствуют такие например вещи как чтение с диска по индексу. Теперь в зависимости от задачи я выбираю либо HDF5 (если мне надо обрабатывать наборы данных, которые не помещаются в памяти) или Feather если мне надо быстро читать/писать полный набор данных (который помещается в памяти). Вполне возможно, что в будущем в Feather Format добавят доступ по индексу или появится другая еще более крутая библиотека и я с удовольствием перейду на неё. Сейчас я изучаю Apache Spark и там точно такая же ситуация - единого стандарта нет и постоянно появляется что-то новое. Некоторые из этих новых форматов выживают и вытесняют устоявшиеся "стандартные" форматы, другие же новые форматы просто вымирают... Если бы был один незыблемый формат, то не было бы никакого развития и усовершенствования.

Ответ 2



Потому, что модули пишет и публикует на PyPI не одна какая-то организация, а множество независимых программистов со всего мира. Каждый из них имеет своё видение какой-либо области, включая сериализацию. Когда какой-то модуль с PyPI кажется мне неудобным или не полностью реализует мои потребности, я пишу свой и публикую.

вторник, 28 января 2020 г.

Сериализация: как сохранить лист объектов своего класса в файл?

#c_sharp #settings #list #сериализация


Точнее говоря, вопрос в том, как сделать это:

Быстро;
Несколькими удобными строчками кода;
Туда и обратно, т.е. сохранять и загружать при запуске приложения;

Желательно сохранять всё это в settings (короче говоря, объекты из листов будут потом
загружаться в контролы, редактироваться из них, и после сохраняться). Проблема только
лишь в том, как их быстро и правильно сохранить.    


Ответы

Ответ 1



Я делал сериализацию в Xml так: public class MyClass { [XmlElement("Name")] public string Name {get; set;} // Это будет элементом [XmlAttribute("Value")] public string Value {get; set;} // Это будет атрибутом [XmlIgnore] public string ServiceField {get; set;} // Это поле мы не хотим сериализовать/десериализовать } public class MyClassCollection { [XmlArray("Collection"), XmlArrayItem("Item")] public List Collection {get; set;} } Затем сериализуем: XmlSerializer xmlSerializer = new XmlSerializer(typeof(MyClassCollection)); StringWriter stringWriter = new StringWriter(); xmlSerializer.Serialize(stringWriter, myClassCollection); // myClassCollection - наши данные После чего получаем xml в виде строки из потока: string xml = stringWriter.ToString(); И десериализуем: var xmlSerializer = new XmlSerializer(typeof (MyClassCollection)); var stringReader = new StringReader(serializedData); MyClassCollection collection = (MyClassCollection) xmlSerializer.Deserialize(stringReader); Небольшие уточнения: Поля для сериализация обязательно должны быть с модификатором public Если Ваше поле имеет простой тип (например string) и Вы хотите его сделать XmlElement'ом, то расставлять атрибуты XmlElement не обязательно, поэтому иногда достаточно расставить XmlIgnore на тех свойсвтах, которые нам не нужны в сериализации/десериализации XmlSerializer может иногда бросать исключение FileNotFoundException и это считается нормальным его поведением, так что просто игнорируйте его. Вот ссылка, на SO, по этому поводу. Надеюсь поможет.

Ответ 2



Проще всего воспользоваться встроенным классом Settings. Единственная тонкость — нужен небольшой хак, чтобы поместить туда список. Вот код: using System; using System.Collections.Generic; using System.Linq; using SettingsTest.Properties; namespace SettingsTest { class Program { static void Main(string[] args) { new Program().RunData(); } List DataValues; void RunData() { // read DataValues = Settings.Default.DataValues; Console.WriteLine("Stored values:"); if (DataValues != null) { foreach (var d in DataValues) Console.WriteLine(d); } // modify Console.Write("Input int values separated by space: "); var input = Console.ReadLine(); DataValues = input.Split() .Select(int.Parse) .Select(i => new Data() { X = i, L = new List() { i - 1, i + 1 } }) .ToList(); // write back Settings.Default.DataValues = DataValues; Settings.Default.Save(); } } [Serializable] public class Data { public int X { get; set; } public List L { get; set; } public override string ToString() { return string.Format("Data(X = {0}, L = <{1}>)", X, string.Join(", ", L)); } } } Для того, чтобы поместить список в Settings, сделайте так: Откройте свойства проекта Перейдите во вкладку Settings. Если в вашем проекте ещё на добавлены Settings, добавьте их. Добавьте свойство с нужным именем (DataValues). Вы не сможете выбрать нужный тип (List), поэтому выберите, например, string. Зайдите в каталог проекта, и найдите там файл Properties\Settings.settings. Откройте его в редакторе, найдите строку

Ответ 3



Ответы прекрасные, плюсую оба, но ребят, девушка во первых просила удобный и быстрый способ. Но результатом вашей подсказки будет несколько классов содержащих свой сериализатор и громоздкий не масштабируемый код. Почему бы не показать сразу красивый вариант с внешним сериализатором, то биш просто вынести его в Extension, например так: public static class SerializeExtension { public static string SerializeToString(this object obj) { var xmlSerializer = new XmlSerializer(obj.GetType()); var stringWriter = new StringWriter(); xmlSerializer.Serialize(stringWriter, obj); return stringWriter.ToString(); } public static T DeserializeString(this string sourceString) { var xmlSerializer = new XmlSerializer(typeof(T)); var stringReader = new StringReader(sourceString); return (T)xmlSerializer.Deserialize(stringReader); } } и вот код xUnit теста для проверки результата: [Fact] public void FirstTest() { var myClass = new MyClassCollection { Collection = new List { new MyClass {Name = "name1", Value = "val1", ServiceField = "bla bla"}, new MyClass {Name = "name2", Value = "val2", ServiceField = "bla bla"}, new MyClass {Name = "name3", Value = "val3", ServiceField = "bla bla"}, new MyClass {Name = "name4", Value = "val4", ServiceField = "bla bla"} } }; var str = myClass.SerializeToString(); Console.WriteLine(str); var res = str.DeserializeString(); Assert.Equal(res.Collection[0].Name, "name1"); Assert.Equal(res.Collection[1].Name, "name2"); Assert.Equal(res.Collection[2].Name, "name3"); Assert.Equal(res.Collection[3].Name, "name4"); Assert.Equal(res.Collection[0].Value, "val1"); Assert.Equal(res.Collection[1].Value, "val2"); Assert.Equal(res.Collection[2].Value, "val3"); Assert.Equal(res.Collection[3].Value, "val4"); }

Ответ 4



[Serializable] class Program { static void Main(string[] args) { Serialize(); Deserialize(); } static void Serialize() { //создаем объект который будет сериализован List words = new List(); words.Add("мир"); words.Add("дверь"); words.Add("мяч"); //откроем поток для записи в файл FileStream fs = new FileStream("file.s", FileMode.Create, FileAccess.Write, FileShare.ReadWrite); BinaryFormatter bf = new BinaryFormatter(); //сериализация bf.Serialize(fs, words); fs.Close(); } static void Deserialize() { List words; FileStream fs = new FileStream("file.s", FileMode.Open, FileAccess.Read, FileShare.Read); BinaryFormatter bf=new BinaryFormatter(); words = (List)bf.Deserialize(fs); fs.Close(); foreach (string w in words) { Console.WriteLine(w); } } } Взято отсюда

воскресенье, 26 января 2020 г.

Как отладить сериализацию под Mono?

#xml #mono #c_sharp #monodevelop #сериализация


Мы пропробовали развернуть наш продукт на Linux-кластере и столкнулись с ошибкой
сериализации. Под отладчиком в MonoDevelop эта ошибка не воспроизводится, только через
командную строку mono. С помощью отладочной печати выяснилось, что внутри сериализатора
(XmlSerializer) возникает InvalidCastException, но что дальше делать - непонятно.
Можно ли хотя бы запустить отладчик под mono при наличии установленного .NET Framework?    


Ответы

Ответ 1



Mono Migration Analyzer (MoMA)

среда, 22 января 2020 г.

Вопрос по сериализации объектов в java

#java #исключения #наследование #сериализация #десериализация


Есть классы, расположенные в определённой иерархии наследования:

public class Program {
    public Program(){
        System.out.println("Текст");
    }
}

public class Wild extends Program{
    public Wild(){
        System.out.println("НеТекст");
    }
}

public class BasicProgramists extends Wild{
    public BasicProgramists(){
        System.out.println("Бесполезная программа");
    }
}

import java.io.*;

public class BasicUser extends BasicProgramists implements Serializable {
    public BasicUser(){
        System.out.println("Использование бесполезной программы");
    }

    public static void main(String[] args) {
        BasicUser user = new BasicUser();
        System.out.println();
        try {
            ObjectOutputStream or = new ObjectOutputStream(new FileOutputStream("Progg.ser"));
            or.writeObject(user);
            or.close();
        }
        catch (IOException ex){
            ex.printStackTrace();
        }

        try {
            ObjectInputStream is = new ObjectInputStream(new FileInputStream("Progg.ser"));
            BasicUser user1 = (BasicUser) is.readObject();
        }
        catch (Exception ex){
            ex.printStackTrace();
        }
    }
}


После компиляции получаем вот такой результат:


До отступа - результат работы конструктора BasicUser

После отступа - результат работы десериализации

Скажите, пожалуйста, почему в результате десириализации, программа не выводит на
экран результат работы конструктора BasicUser, хотя выводит результат работы всех родительских
классов?
    


Ответы

Ответ 1



Сериализация в java не использует конструкторы для создания объектов. Поэтому контруктор не был вызван. Но почему вызвались кострукторы родительских классов? Очевидно, т.к. они не реализовали Serializable, то механизм сериализации о них ничего не знал и поэтому инициализация происходила обычным способом - через конструктор. В сказаном легко убедиться реализовав Serializable классом Program.

Ответ 2



В дополнение к предыдущему ответу Если нужно, чтобы все конструкторы отрабатывали Немного допилил ваш код import java.io.*; class Program { Program() { System.out.println("Текст"); } } class Wild extends Program { Wild() { System.out.println("НеТекст"); } } class BasicProgrammers extends Wild { BasicProgrammers() { System.out.println("Бесполезная программа"); } } class BasicUser extends BasicProgrammers { BasicUser() { System.out.println("Использование бесполезной программы"); } } class SuperUser extends BasicUser implements Serializable { public static void main(String[] args) { SuperUser superUser = new SuperUser(); System.out.println(); try { ObjectOutputStream or = new ObjectOutputStream(new FileOutputStream("Progg.ser")); or.writeObject(superUser); or.close(); } catch (IOException ex) { ex.printStackTrace(); } try { ObjectInputStream is = new ObjectInputStream(new FileInputStream("Progg.ser")); SuperUser superUser1 = (SuperUser) is.readObject(); } catch (Exception ex) { ex.printStackTrace(); } } } Вывод: // Текст // НеТекст // Бесполезная программа // Использование бесполезной программы // // Текст // НеТекст // Бесполезная программа // Использование бесполезной программы // // Process finished with exit code 0

вторник, 31 декабря 2019 г.

Как пометить класс как [Serializable] ,если он подгружается из dll

#c_sharp #сериализация #serializable


Я использую dll файлы в моем проекте. Как пометить класс как сериализуемый атрибутом
[serializable].
Я не имею исходного кода, только dll файл, на который есть ссылка в моем классе.
Я хочу сделать его сериализуемым, чтобы использовать состояние представления для
моего объекта, который использует dll. Использую бинарную сериализацию.
Как решить проблему?
    


Ответы

Ответ 1



Если вы можете вручную, кодом, извлечь из такого объекта все нужные свойства, то можно использовать механизм Surrogate Selector. Код честно взят из MSDN. Employee - тот самый "внешний" класс, на который нельзя поставить [Serializable]: using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters.Binary; // This class is not serializable. class Employee { public String name, address; public Employee(String name, String address) { this.name = name; this.address = address; } } // This class can manually serialize an Employee object. sealed class EmployeeSerializationSurrogate : ISerializationSurrogate { // Serialize the Employee object to save the object's name and address fields. public void GetObjectData(Object obj, SerializationInfo info, StreamingContext context) { Employee emp = (Employee) obj; info.AddValue("name", emp.name); info.AddValue("address", emp.address); } // Deserialize the Employee object to set the object's name and address fields. public Object SetObjectData(Object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { Employee emp = (Employee) obj; emp.name = info.GetString("name"); emp.address = info.GetString("address"); return null; } } public sealed class App { static void Main() { // This sample uses the BinaryFormatter. IFormatter formatter = new BinaryFormatter(); // Create a MemoryStream that the object will be serialized into and deserialized from. using (Stream stream = new MemoryStream()) { // Create a SurrogateSelector. SurrogateSelector ss = new SurrogateSelector(); // Tell the SurrogateSelector that Employee objects are serialized and deserialized // using the EmployeeSerializationSurrogate object. ss.AddSurrogate(typeof(Employee), new StreamingContext(StreamingContextStates.All), new EmployeeSerializationSurrogate()); // Associate the SurrogateSelector with the BinaryFormatter. formatter.SurrogateSelector = ss; try { // Serialize an Employee object into the memory stream. formatter.Serialize(stream, new Employee("Jeff", "1 Microsoft Way")); } catch (SerializationException e) { Console.WriteLine("Serialization failed: {0}", e.Message); throw; } // Rewind the MemoryStream. stream.Position = 0; try { // Deserialize the Employee object from the memory stream. Employee emp = (Employee) formatter.Deserialize(stream); // Verify that it all worked. Console.WriteLine("Name = {0}, Address = {1}", emp.name, emp.address); } catch (SerializationException e) { Console.WriteLine("Deserialization failed: {0}", e.Message); throw; } } } } // This code produces the following output. // // Name = Jeff, Address = 1 Microsoft Way т.е. сериализоваться будет класс без аттрибута [Serializable], но как именно его сериализовать - вам придется описать самому.

Ответ 2



Атрибуты класса лежат в метаданных, которые лежат внутри скомпилированной сборки, то есть пометить класс как [Serializable] у вас не получится. Пользуйтесь сериализатором, который не смотрит на эти аттрибуты.

воскресенье, 29 декабря 2019 г.

Десериализация многих разных JSON на C#

#c_sharp #json #сериализация


Есть около сотни (в будущем больше) JSON. Каждый JSON имеет свою структуру. Необходимо
их все десериализовать. Пытаюсь использовать стандартный класс DataContractJsonSerializer,
пользуюсь этой схемой.

При создании экземпляра класса нужно передавать ему тип сериализуемых или десериализуемых
экземпляров

DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(Person));


Как я уже сказал, у меня много разных типов, но метод, осуществляющий десериализацию,
один. У меня есть строковые параметры с именами классов, описывающих структуру JSON,
например: "Person", "Address" и прочее.

Как их подставить в конструктор DataContractJsonSerializer так, чтобы не писать сотню
кейсов? Возможно ли это стандартными методами?

Пробовал это, но не работает.

Как вообще такие задачи решаются в промышленной среде?

В интернете полно примеров с одним жестко заданным JSON, но про много разных нигде
не нашел. 
    


Ответы

Ответ 1



Если вам изначально известно, какой объект представлен в json'e, то вам достаточно воспользоваться Generic-методом для дессериализации: var myNewObject = JsonConvert.DeserializeObject(json);

Ответ 2



Вам стоит воспользоваться JSON.NET (вот так подключается к проекту). После этого, чтобы не выписывать тип, вы можете десериализовать в JObject: using Newtonsoft.Json; using Newtonsoft.Json.Linq; string json = "{\n" + " \"access_token\":\"13a85707de16fbb1c290250872f30e0b\",\n" + " \"error\":\n" + " { \"code\": \"0000\", \"text\": \"ok\" },\n" + " \"duration\": 0.025489807128906\n" + "}"; var o = JObject.Parse(json); Если вы знаете имя свойства, им можно легко воспользоваться: var accessToken = (string)o["access_token"]; // "13a85707de16fbb1c290250872f30e0b" var errorCode = (int)o["error"]["code"]; // 0 var duration = TimeSpan.FromSeconds((double)o["duration"]); // {00:00:00.0250000}

Ответ 3



Если приложению доступна сборка, в которой находится нужный класс, и у вас есть имя этого класса, вы можете загрузить assembly, найти в ней соответствующий тип, а затем передать его в конструктор DataContractJsonSerializer: using System; using System.Reflection; using System.Runtime.Serialization.Json; AssemblyName asn = new AssemblyName("Assembly.Name"); Assembly assembly = Assembly.Load(asn); Type tp = assembly.GetType("Person"); if (tp == null) throw new TypeAccessException(); var ser = new DataContractJsonSerializer(tp); object person = ser.ReadObject(stream); // => Person

Ответ 4



public static T FromJson(this string objString) { var stream = objString.GetMemoryStream(); stream.Position = 0; var ser = new DataContractJsonSerializer(typeof(T)); return (T)ser.ReadObject(stream); } public static string ToJson(this T obj) { var stream = new MemoryStream(); var ser = new DataContractJsonSerializer(typeof(T)); ser.WriteObject(stream, obj); stream.Position = 0; var sr = new StreamReader(stream); return sr.ReadToEnd(); } Одно НО: не работает с многомерными массивами.

четверг, 26 декабря 2019 г.

Qt. Как сериализовать объекты содержащие коллекции?

#cpp #qt #сериализация


Имеются вот такие вот классы:

class Institution //Класс "учебное заведение"
{
    ...
private:
    ...
    QList Pupils; //Коллекция учеников
};

class Pupil //Класс "ученик"
{
    ...
private:
    ...
    QList Lessons; //Коллекция предметов
};

class Exam //Класс "экзамен"
{
    ...
};


Необходимо сериализовать коллекцию объектов класса Institution (хотя я не уверен
уместно ли называть это сериализацией), а именно сохранить это коллекцию в бинарник
и потом "взять" её оттуда.

Сохранение (здесь проблем нет, т.е. sizeof работает как надо):

QList institutions;
...
Institution bf("", 0);
char str[1024];
strcpy(str, filename.toStdString().c_str());
std::ofstream out(str, std::ios::out | std::ios::binary);
for (int i = 0; i < institutions.size(); i++)
{
    bf = *institutions[i];
    int a = sizeof bf;
    out.write((char *) &bf, sizeof bf);
}
out.close();


Считывание (здесь, понятное дело, sizeof не работает, т.к. размер вложенных коллекций
заранее не определить):

QList institutions;
...
institutions.clear();
char str[1024];
strcpy(str, filename.toStdString().c_str());
std::ifstream in(str, std::ios::in | std::ios::binary);
while (!in.eof() && in.peek() != EOF)
{
    Institution *bf = new Institution("", 0);
    int a = sizeof bf;
    in.read((char *) bf, sizeof *bf); //!
    institutions.append(bf);
}
in.close();


Теперь и вопрос, как всё-таки можно записать и считать эту коллекцию в/из бинарника,
не изменяя структуру классов(!)?

Или может быть есть что-то подобное как в Java?

/*Коллекция*/
ArrayList list = new ArrayList();
/*Запись*/
try
{
    FileOutputStream fos = new FileOutputStream(file.toString());
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(list);
    oos.close();
    fos.close();
}
catch(IOException ioe)
{
}

/*Считывание*/
FileInputStream fis = new FileInputStream(file.toString());
ObjectInputStream ois = new ObjectInputStream(fis);
try
{
    list = (ArrayList) ois.readObject();
} 
catch (ClassNotFoundException e)
{
}
ois.close();
fis.close();


UPD#1:
Всё решилось использованием QDataStream, заменой всех коллекций с указателями на
объекты классов на коллекции с объектами классов и перегрузкой operator <<, operator
>> для QDataStream:

QDataStream &operator << (QDataStream &out, const Institution &s);
QDataStream &operator >> (QDataStream &in, Institution &s);
QDataStream &operator << (QDataStream &out, const Pupil &s);
QDataStream &operator >> (QDataStream &in, Pupil &s);
QDataStream &operator << (QDataStream &out, const Exam &s);
QDataStream &operator >> (QDataStream &in, Exam &s);


Пример реализации:

QDataStream &operator << (QDataStream &out, const Institution &s)
{
    out << s.getName() << s.getID() << s.getPupils();
    return out;
}

QDataStream &operator >> (QDataStream &in, Institution &s)
{
    QString Name;
    int ID;
    QList Pupils;
    in >> Name >> ID >> Pupils;
    s = Institution(Name, ID, Pupils);
    return in;
}

    


Ответы

Ответ 1



Всё решилось использованием QDataStream, заменой всех коллекций с указателями на объекты классов на коллекции с объектами классов и перегрузкой operator <<, operator >> для QDataStream: QDataStream &operator << (QDataStream &out, const Institution &s); QDataStream &operator >> (QDataStream &in, Institution &s); QDataStream &operator << (QDataStream &out, const Pupil &s); QDataStream &operator >> (QDataStream &in, Pupil &s); QDataStream &operator << (QDataStream &out, const Exam &s); QDataStream &operator >> (QDataStream &in, Exam &s); Пример реализации: QDataStream &operator << (QDataStream &out, const Institution &s) { out << s.getName() << s.getID() << s.getPupils(); return out; } QDataStream &operator >> (QDataStream &in, Institution &s) { QString Name; int ID; QList Pupils; in >> Name >> ID >> Pupils; s = Institution(Name, ID, Pupils); return in; }

воскресенье, 22 декабря 2019 г.

Возможна ли сериализация несериализуемого объекта?

#c_sharp #сериализация


При попытке сериализовать такой объект программа валится с ошибкой:


  Тип CookComputing.XmlRpc.XmlRpcStruct в сборке CookComputing.XmlRpcV2,
  Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d6e17aa302004d не
  отмечен как сериализуемый.


можно ли как то обойти это и сериализовать объект такого типа?
У меня нет доступа к изменению свойств этого типа.

Буду рада, если мне подскажете например другие способы распаковать и увидеть то что
пришло от сервера

[XmlRpcUrl("https://blabala.com/rpc")] // описание методов
public interface Trac : IXmlRpcProxy            
{       
    [XmlRpcMethod("ticket.get")]  //
    object[] get(int id);

    [XmlRpcMethod("ticket.getActions")]  //
    object[] getActions(int id);
}

public partial class DHL : Form
{
    public DHL()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        int id = 6;                  
        get(id);
    }

    private void get(int id)
    {
        Trac proxy;
        string user = "uzer";
        string password = "parol";
        proxy = XmlRpcProxyGen.Create();
        proxy.Credentials = new System.Net.NetworkCredential(user, password);
         object[] arr = proxy.get(id);
         BinaryFormatter formatter = new BinaryFormatter();
        using (FileStream fs = new FileStream("arr.dat", FileMode.OpenOrCreate))
          {
              formatter.Serialize(fs, arr);// тут и ошибка, что Тип CookComputing.XmlRpc.XmlRpcStruct
в сборке CookComputing.XmlRpcV2, Version=3.0.0.0, Culture=neutral, PublicKeyToken=a7d6e17aa302004d
не отмечен как сериализуемый.
          }
    }

    private void button4_Click(object sender, EventArgs e)
    {
        int id = 6;            
        getActions( id);
    }

    private void getActions(int id)
    {
        Trac proxy;
        string user = "uzer";
        string password = "parol";
        proxy = XmlRpcProxyGen.Create();
        proxy.Credentials = new System.Net.NetworkCredential(user, password);
        object[]  arr = proxy.getActions(id);
        BinaryFormatter formatter = new BinaryFormatter();
        using (FileStream fs = new FileStream("arr.dat", FileMode.OpenOrCreate))
        {
        formatter.Serialize(fs, arr);//а тут нет такой ошибки
        }
    }
}

    


Ответы

Ответ 1



Нашел решение на EnSO, далее вольный перевод: Можно создать serialization surrogate. Например, у нас есть такой класс public class Person { public string Name { get; set; } public int Age { get; set; } public DriversLicense License; } // An instance of this type will be part of the object graph and will need to be // serialized also. public class DriversLicense { public string Number { get; set; } } Необходимо создать суррогат для каждого объекта в графе объектов. Для этого необходимо реализовать интерфейс ISerializationSurrogate public class PersonSurrogate : ISerializationSurrogate { /// /// Manually add objects to the store. /// public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { Person person = (Person) obj; info.AddValue("Name", person.Name); info.AddValue("Age", person.Age); info.AddValue("License", person.License); } /// /// Retrieves objects from the store. /// /// public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { Person person = (Person)obj; person.Name = info.GetString("Name"); person.Age = info.GetInt32("Age"); person.License = (DriversLicense) info.GetValue("License", typeof(DriversLicense)); return person; } } public class DriversLicenseSurrogate : ISerializationSurrogate { /// /// Manually add objects to the store. /// public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) { DriversLicense license = (DriversLicense)obj; info.AddValue("Number", license.Number); } /// /// Retrieves objects from the store. /// /// public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) { DriversLicense license = (DriversLicense)obj; license.Number = info.GetString("Number"); return license; } } Далее тебе нужен IFormatter, который знает о твоих суррогатах им им нужно инициализировать SurrogateSelector: private static void SerializePerson(Person person) { if (person == null) throw new ArgumentNullException("person"); using (var memoryStream = new MemoryStream()) { //Configure our surrogate selectors. var surrogateSelector = new SurrogateSelector(); surrogateSelector.AddSurrogate(typeof (Person), new StreamingContext(StreamingContextStates.All), new PersonSurrogate()); surrogateSelector.AddSurrogate(typeof (DriversLicense), new StreamingContext(StreamingContextStates.All), new DriversLicenseSurrogate()); //Serialize the object IFormatter formatter = new BinaryFormatter(); formatter.SurrogateSelector = surrogateSelector; formatter.Serialize(memoryStream, person); //Return to the beginning of the stream memoryStream.Seek(0, SeekOrigin.Begin); //Deserialize the object Person deserializedPerson = (Person) formatter.Deserialize(memoryStream); } } Если необходимо сериализовать защищенные и приватные поля, то задача может решаться несколько сложнее. Подозреваю, что придется пользоваться рефлексией и ручками брать\присваивать значения из недоступных свойств... Еще интересное мнение по этому поводу нашел: Суть в том, что не всегда-это реализуемо. Например, контролы Windows Forms хранят хэндлы ОС=> при десериализации на другой машине все скорее всего упадет. Наверное, можно и это учесть с помощью WinApi, но это уже изврат. Рихтер в своей книге хорошо описывает процесс сераиализации несериализуемых объектов. В том числе приводится пример с суррогатами.

Ответ 2



Нашла решение своей задачи с помощью DataTable object[] arr = proxy.get(id); //момент получения данных DataTable dt = new DataTable(); XmlRpcStruct arr3 = (XmlRpcStruct)arr[3];//проблема у меня была с выводом 3 его элемента объекта, поэтому его и преобразовываем XmlRpcStruct[] arr33 = new XmlRpcStruct[1]; arr33[0] = arr3; dt = StructArrayToDT(arr33);//преобразуем в DataTable //DA.Fill(dt); dataGridView1.DataSource = dt;//полученный DataTable можно вывести в DataGrid textBox1.Text = textBox1.Text + arr[0] + "\r\n" + arr[1] + "\r\n" + arr[2] + "\r\n";// тут выводим 0-2 члены объекта, с которыми не было проблем BinaryFormatter formatter = new BinaryFormatter();//можно сериализовать полученный DataTable using (FileStream fs = new FileStream("arr.dat", FileMode.OpenOrCreate)) { formatter.Serialize(fs, dt); } public static DataTable StructArrayToDT(XmlRpcStruct[] data) //метод который из XmlRpcStruct[] делает DataTable { DataTable dt = new DataTable(); if (data.Length == 0) { return dt; } // do columns foreach (DictionaryEntry d in data[0]) { dt.Columns.Add(d.Key.ToString(), typeof(object)); } foreach (XmlRpcStruct xmlstruct in data) { DataRow dr = dt.NewRow(); foreach (DictionaryEntry d in xmlstruct) { try { dr[d.Key.ToString()] = d.Value; } catch (Exception ex) { // handle errors } } dt.Rows.Add(dr); } return dt; }

пятница, 20 декабря 2019 г.

Xml сериализация объектов со свойствами только для чтения

#c_sharp #xml #сериализация




При сериализации массива из объектов этого класса все атрибуты игнорируются, если
у них нет setter'а. Вопрос: как решить проблему с этим явлением; класс не предусмотрен
для изменения данных.

/// 
/// Представляет поля и методы для работы с абстракцией осциллятора.
/// 
[Serializable]
public class Oscillator : IMixer
{
    private Boolean enable;
    /// 
    /// Состояние вкл / выкл.
    /// 
    [XmlAttribute]
    public Boolean Enable
    {
        get
        {
            return enable;
        }
        set
        {
            enable = value;
        }
    }


    private Int32 volume;
    /// 
    /// Процентная громкость.
    /// 
    [XmlAttribute]
    public Int32 Volume
    {
        get
        {
            return volume;
        }
    }



    public Oscillator()
    {

    }


    public Oscillator(Boolean enable, Int32 volume)
    {
        this.enable = enable;
        this.volume = volume;
    }
}


    //Вот, сама сериализация, если интересно.
    /// 
    /// Сохраняет пресет в Xml-файл.
    /// 
    /// 
    /// Путь к файлу.
    /// 
    /// 
    /// Массив осцилляторов для записи его в пресет.
    /// 
    public void SavePreset(String fileName, Oscillator[] oscs)
    {
        try
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Oscillator[]));
            using (Stream stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
            {
                serializer.Serialize(stream, oscs);
                //for (Int32 i = 0; i < oscs.Length; i++)
                //{
                //    serializer.Serialize(stream, oscs[i]);
                //}
            }
        }
        catch
        {
            throw new PresetException(PresetException.SaveError);
        }
    }

    


Ответы

Ответ 1



Никак, это ограничение XmlSerializer. Only public properties and fields can be serialized. Properties must have public accessors (get and set methods). If you must serialize non-public data, use the DataContractSerializer class rather than XML serialization. В оригинальном ответе, как и в документации, советуют воспользоваться более гибким DataContractSerializer. Вариант с вырожденным сеттером, думаю, рассматривать не будем.

Ответ 2



Никак. Но вам это и не нужно. Вы должны иметь отдельные классы для сериализации/десериализации (и вообще любой транспортировки данных - DTO) и для бизнес-логики (POCO). И я рекомендую вам реализовать такое разделение как можно раньше, это позволит избежать глобальных ломающих изменений по мере развития проекта. Ссылка по теме: Наглядный пример различия DTO, POCO (POJO) и Value Object

среда, 18 декабря 2019 г.

Архитектура приложения Windows Forms на C# (N мониторов, N форм)

#c_sharp #xml #winforms #архитектура #сериализация


Пытаюсь создать приложение на C# Windows Forms, в котором есть N мониторов и N форм.

Формы делятся на два типа:
1) Главная форма (может быть только одна);
2) Дополнительная форма (максимальное количество N-1).

При запуске приложения открывается главная форма, у которой есть кнопка "Открыть",
при нажатии на которую мы можем создать дополнительную форму.

На одном мониторе может находиться только одна форма и при нажатии на кнопку "Открыть",
мы должны проверять есть ли свободные мониторы. Если есть, то помещаем новую форму
на свободный монитор, если таковых нет, то выводим сообщение об этом.

Цель приложения:
1) При закрытии, для всех форм: сохранить Width, Height, Location.X, Location.Y и
номер монитора, на котором находится форма;
2) При открытии, выгрузка сохранённых параметров, если таковые есть;
3) Осуществлять контроль свободных и занятых мониторов.



Для сохранения и выгрузки я использую класс XmlSerializer, вся информация о мониторах
есть в классе Screen. С главной формой всё работает, но трудности возникают со множеством
форм. Как лучше всего продумать иерархию классов, чтобы все данные можно было сериализовать
в один файл? Где хранить информацию (Width, Height, Location.X, Location.Y, номер монитора)
о всех формах и где отслеживать события (создание, перемещение, изменение размера)
связанные с ними? Может использовать паттерн MVC?

Надеюсь, получилось доходчиво объяснить суть вопроса. Заранее спасибо за советы,
решения, наводки.
    


Ответы

Ответ 1



Первое, что приходит в голову - создание специального класса, который будет решать задачу по управлению нужными данными для форм в том числе и их сериализацию/десереализацию. Т.е. вероятно, вам нужна некая коллекция, в которой хранятся нужные параметры каждой формы, и реализован механизм сериализации этой коллекции в этот самый один файл. Варианты разные технически могут быть (через массив, List или еще как), суть одна - имеем коллекцию в которой каждый элемент - есть набор данных по конкретной форме. Соответственно, должны быть методы, которые могут работать с этими данными (например, считывать значения из формы и устанавливать их форме), находить нужную форму, загружать в/из XML, обработчики событий.... Потому и напрашивается класс, так как нужен механизм по хранению самих данных, так и набор методов для работы с этими данными. В качестве дальнейшего развития я бы дополнительно сделал класс базовой формы, где были бы прописаны (например, в обработчиках событий) нужные действия (например, отслеживание изменений размеров и положения формы). Таким образом сама форма умела бы работать с коллекцией, и при её изменении она сама бы автоматически записывала данные в "свой" элемент коллекции. Тут же можно было бы сделать механизмы валидации, т.е. проверки, а не занят ли монитор другой формой и что делать, если занят и т.д. Естественно, тогда все остальные формы были бы унаследованы от базовой. Либо, несколько иной вариант, это вызывать методы нашего объекта по считыванию и записи информации из коллекции только тогда, когда это необходимо (запуск и завершение программы). Но тут не решается вопрос с автоматической валидацией (проверками дополнительных условий). Т.е. если мы читаем и пишем данные руками, то и вызов проверяющих методов тоже нужно делать будет руками. Более того, возможно, я бы создал еще и интерфейс, который объявлял бы все методы по работе с данными, это позволило бы делать разные и независимые друг от друга варианты реализации.

воскресенье, 15 декабря 2019 г.

Как перевести `VB.NET` код c лямбда-выражениями в код с циклами

#net #vbnet #сериализация #jsonnet


Проект под .NET 3.5 в Visual Studio 2008, это продолжение этого вопроса про сериализацию/десериализацию
и динамически меняющиеся свойства JSON.

В этом ответе имеется решение вопроса несовместимости форматов сериализаторов, которое
состоит в том что по умолчанию Newtonsoft.Json сохраняет публичные свойства в JSON,
а DataContractJsonSerializer сохраняет приватные свойства. Соответственно сериализация
одного и того же объекта дает разный результат.

Для DataContractJsonSerializer

{
  "ExProperty": {
    "_disabled": false,
    "_el_class": null,
    "_enum_values": [],
    "_id": "124",
    "_inline_script": null,
    "_inline_style": null,
    "_innerHTML": null,
    "_input_max": 0,
    "_input_min": 0,
    "_input_min_zero": false,
    "_input_subtype": null,
    "_nolabel": true,
    "_onclick": null,
    "_order": 0,
    "_parent_wrapp_class": "rrrrrrr",
    "_placeholder": null,
    "_value": null,
    "_width": 0,
    "_wrapp_class": null,
    "_wrapp_col_md": 55,
    "_wrapp_label_class": "eeee",
    "_dt_append": null,
    "_dt_append_name": null,
    "_dt_name": null,
    "_fieldSet": [
      "qqqqqqqqqqqqq",
      "aaaaaaaaaaa",
      "ssssssssssssss",
      "dddddddddddddd",
      "fgfffffffffffffffffff"
    ],
    "_fieldset_name": "aaaaaaaaaaa",
    "_isfiltr": false,
    "_lbl": null,
    "_lbl_short": null,
    "_lkp_filter": null,
    "_lkp_idas": null,
    "_lkp_nameas": null,
    "_lkp_table": null,
    "_req": false,
    "_sh_in_add": false,
    "_sh_in_edit": false,
    "_sh_in_list": false,
    "_type": null
  },
  "PropertyOne": "значение 1",
  "PropertyThree": null,
  "PropertyTwo": "значение 2"
}


Для Newtonsoft.Json

{
  "PropertyOne": "значение 1",
  "PropertyTwo": "значение 2",
  "PropertyThree": null,
  "ExProperty": {
    "fieldSets": [
      "qqqqqqqqqqqqq",
      "aaaaaaaaaaa",
      "ssssssssssssss",
      "dddddddddddddd",
      "fgfffffffffffffffffff"
    ],
    "fieldset_name": "aaaaaaaaaaa",
    "lbl": null,
    "lbl_short": null,
    "type": 0,
    "req": false,
    "lkp_table": null,
    "lkp_nameas": null,
    "lkp_idas": null,
    "lkp_filter": null,
    "dt_name": null,
    "dt_append_name": null,
    "dt_append": null,
    "sh_in_edit": false,
    "sh_in_add": false,
    "sh_in_list": false,
    "isfiltr": false,
    "order": 0,
    "wrapp_col_md": 55,
    "parent_wrapp_class": "rrrrrrr",
    "wrapp_class": null,
    "wrapp_label_class": "eeee",
    "el_class": null,
    "value": null,
    "innerHTML": null,
    "placeholder": null,
    "nolabel": true,
    "width": 0,
    "onClick": null,
    "inlineStyle": null,
    "Disabled": false,
    "Id": "124",
    "inlineScript": null,
    "inputSubtype": null,
    "InputMinZero": false,
    "inputMin": 0,
    "inputMax": 0,
    "EnumValues": []
  }
}


Требуется переписать класс из ответа, так чтобы он компилировался в 2008 студии.

Imports System.Reflection
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Serialization

Public Class MyContractResolver
    Inherits DefaultContractResolver
    Protected Overrides Function CreateProperties(type As Type, memberSerialization
As MemberSerialization) As IList(Of JsonProperty)
        Dim props = type.GetProperties(BindingFlags.Public Or BindingFlags.NonPublic
Or BindingFlags.Instance).
                            Select(Function(p) MyBase.CreateProperty(p, memberSerialization)).
                            Union(type.GetFields(BindingFlags.Public Or BindingFlags.NonPublic
Or BindingFlags.Instance).
                            Select(Function(f) MyBase.CreateProperty(f, memberSerialization))).ToList()

        props.ForEach(Sub(p)
                          p.Writable = True
                          p.Readable = True
                      End Sub)
        Return props
    End Function
End Class


Сейчас код не воспринимается, ибо нет таких конструкций в 2008 версии.


    


Ответы

Ответ 1



Попробуйте такой вариант, должно сработать: Imports System.Reflection Imports Newtonsoft.Json Imports Newtonsoft.Json.Serialization Public Class MyContractResolver Inherits DefaultContractResolver Protected Overrides Function CreateProperties(ByVal type As Type, ByVal memberSerialization As MemberSerialization) As IList(Of JsonProperty) Dim props As New List(Of JsonProperty)() Dim propsInfo As PropertyInfo() = type.GetProperties(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance) For Each pi As PropertyInfo In propsInfo Dim jp = MyBase.CreateProperty(pi, memberSerialization) If Not props.Contains(jp) Then props.Add(jp) End If Next Dim fieldsInfo As FieldInfo() = type.GetFields(BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance) For Each pi As FieldInfo In fieldsInfo Dim jp = MyBase.CreateProperty(pi, memberSerialization) If Not props.Contains(jp) Then props.Add(jp) End If Next For Each p As JsonProperty In props p.Writable = True p.Readable = True Next Return props End Function End Class

Ответ 2



Видимо, VB2008 ещё не понимал многострочные лямбды, поэтому соответствующую функцию можно сделать обычной, либо просто сделать обход списка циклом: For Each p In props p.Writable = True p.Readable = True Next p

суббота, 15 июня 2019 г.

Десериализация json-строки в Словарь (Dictionary)


{ "rates_scores_stats": [{ "name": 10, "value": 3545 }, { "name": 9, "value": 1004 }, { "name": 8, "value": 820 }, { "name": 7, "value": 493 }, { "name": 6, "value": 218 }, { "name": 5, "value": 138 }, { "name": 4, "value": 80 }, { "name": 3, "value": 41 }, { "name": 2, "value": 26 }, { "name": 1, "value": 83 } ], "rates_statuses_stats": [{ "name": "Запланировано", "value": 2506 }, { "name": "Смотрю", "value": 7861 }, { "name": "Просмотрено", "value": 1947 }, { "name": "Отложено", "value": 1443 }, { "name": "Брошено", "value": 1358 } ] }
Есть JSON описанный выше. Для десериализации я использую Newtonsoft.Json. В частности, метод JsonConvert.DeserializeObject(json)
Я знаю, что для этого json можно описать типичный класс следующим образом

Тоже самое текстом: http://pastexen.com/code.php?file=aMvORa6As3.cs
Но мне надо превратить это не в коллекцию вспомогательных классов или же коллекцию KeyValuePair. Мне нужен именно словарь, чтобы я мог обратиться к такому объекту следующим образом:
var result = JsonConvert.DeserializeObject(json); var val = result.rates_scores_stats[10]; // val = 3545 var val1 = result.rates_statuses_stats["Смотрю"]; // val1 = 7861
Можно ли как-нибудь сериализовать это в словарь? Может с помощью get/set как-то извернуться?
P.S.: Я знаю, что в шарпе есть Linq, который позволит сделать что-то типо result.FirstOrDefault(x.value => x.name == "10"), но я пишу переносимую библиотеку и хочу упростить доступ к данным =/


Ответ

Например, можно десериализовать в JObject, и потом сконвертировать данные вручную:
var obj = JObject.Parse(json); var rates_scores_stats = ((JArray)obj["rates_scores_stats"]).ToDictionary(entry => (int)entry["name"], entry => (int)entry["value"]); var rates_statuses_stats = ((JArray)obj["rates_statuses_stats"]).ToDictionary(entry => (string)entry["name"], entry => (int)entry["value"]); var result = new MyClass() { rates_scores_stats = rates_scores_stats, rates_statuses_stats = rates_statuses_stats };

Альтернативный ответ здесь. Немного модифицировав его, можно получить менее «ручное» решение.
Создадим отдельный конвертер (спёрт и модифицирован из ответа по ссылке):
public class JsonGenericDictionaryOrArrayConverter : JsonConverter { // поскольку у вас в JSON'е не key/value, а name/value, нужен специальный класс // для десериализации одного элемента массива class NameValuePair { public N name { get; set; } public V value { get; set; } }
public override bool CanConvert(Type objectType) { return GetDictionaryKeyValueTypes(objectType).Count() == 1; }
public override bool CanWrite { get { return false; } }
object ReadJsonGeneric( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var tokenType = reader.TokenType;
var dict = existingValue as IDictionary; if (dict == null) { var contract = serializer.ContractResolver.ResolveContract(objectType); dict = (IDictionary)contract.DefaultCreator(); }
if (tokenType == JsonToken.StartArray) { var pairs = new JsonSerializer() .Deserialize[]>(reader); if (pairs == null) return existingValue; foreach (var pair in pairs) dict.Add(pair.name, pair.value); } else if (tokenType == JsonToken.StartObject) { // Using "Populate()" avoids infinite recursion. // https://github.com/JamesNK/Newtonsoft.Json/blob/ // ee170dc5510bb3ffd35fc1b0d986f34e33c51ab9/Src/Newtonsoft.Json/Converters/ // CustomCreationConverter.cs serializer.Populate(reader, dict); } return dict; }
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // Throws an exception if not exactly one. var keyValueTypes = GetDictionaryKeyValueTypes(objectType).Single();
var method = GetType().GetMethod( "ReadJsonGeneric", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); var genericMethod = method.MakeGenericMethod( new[] { keyValueTypes.Key, keyValueTypes.Value }); return genericMethod.Invoke( this, new object[] { reader, objectType, existingValue, serializer }); }
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); }
static IEnumerable> GetDictionaryKeyValueTypes(Type type) { foreach (Type intType in GetInterfacesAndSelf(type)) { if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { var args = intType.GetGenericArguments(); if (args.Length == 2) yield return new KeyValuePair(args[0], args[1]); } } }
static IEnumerable GetInterfacesAndSelf(Type type) { if (type == null) throw new ArgumentNullException(); if (type.IsInterface) return new[] { type }.Concat(type.GetInterfaces()); else return type.GetInterfaces(); } }
Имея этот вспомогательный класс, можно десериализовать просто так:
var settings = new JsonSerializerSettings { Converters = { new JsonGenericDictionaryOrArrayConverter() } }; var result = JsonConvert.DeserializeObject(jsonString, settings);

Обновление: Для portable library (.NET 4.5.1 + Windows Universal 8.1 + Windows Phone 8.1) у меня скомпилировалось такое:
public class JsonGenericDictionaryOrArrayConverter : JsonConverter { // поскольку у вас в JSON'е не key/value, а name/value, нужен специальный класс // для десериализации одного элемента массива class NameValuePair { public N name { get; set; } public V value { get; set; } }
public override bool CanConvert(Type objectType) { return GetDictionaryKeyValueTypes(objectType).Count() == 1; }
public override bool CanWrite { get { return false; } }
public object ReadJsonGeneric( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var tokenType = reader.TokenType;
var dict = existingValue as IDictionary; if (dict == null) { var contract = serializer.ContractResolver.ResolveContract(objectType); dict = (IDictionary)contract.DefaultCreator(); }
if (tokenType == JsonToken.StartArray) { var pairs = new JsonSerializer() .Deserialize[]>(reader); if (pairs == null) return existingValue; foreach (var pair in pairs) dict.Add(pair.name, pair.value); } else if (tokenType == JsonToken.StartObject) { // Using "Populate()" avoids infinite recursion. // https://github.com/JamesNK/Newtonsoft.Json/blob/ // ee170dc5510bb3ffd35fc1b0d986f34e33c51ab9/Src/Newtonsoft.Json/Converters/ // CustomCreationConverter.cs serializer.Populate(reader, dict); } return dict; }
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // Throws an exception if not exactly one. var keyValueTypes = GetDictionaryKeyValueTypes(objectType).Single();
var method = GetType().GetTypeInfo().GetDeclaredMethod("ReadJsonGeneric"); var genericMethod = method.MakeGenericMethod( new[] { keyValueTypes.Key, keyValueTypes.Value }); return genericMethod.Invoke( this, new object[] { reader, objectType, existingValue, serializer }); }
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer) { throw new NotSupportedException(); }
static IEnumerable> GetDictionaryKeyValueTypes(Type type) { foreach (Type intType in GetInterfacesAndSelf(type)) { if (intType.GetTypeInfo().IsGenericType && intType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { var args = intType.GetTypeInfo().GenericTypeArguments; // вроде бы именно GenericTypeArguments, а не Parameters if (args.Length == 2) yield return new KeyValuePair(args[0], args[1]); } } }
static IEnumerable GetInterfacesAndSelf(Type type) { if (type == null) throw new ArgumentNullException(); if (type.GetTypeInfo().IsInterface) return new[] { type }.Concat(type.GetTypeInfo().ImplementedInterfaces); else return type.GetTypeInfo().ImplementedInterfaces; } }

суббота, 8 июня 2019 г.

Теряется ссылка на один и тот же объект при сериализации-десериализации через DataContract

Пытаюсь сериализовать сложный объект через DataContract. Объект имеет список подобъектов, которые имеют в свою очередь свой список (назовем - листья дерева). К этим листьям, например, первого элемента списка основного объекта, можно добраться их других элементов списка (ссылка одна и та же). Получается довольно сложный граф, в котором много связей.
При десериализации у каждого элемента списка - свои листья, т.е. ссылки теряются, если в объекте источнике - это один и тот же лист, то после десериализации - просто копия.
Примерно так
A A / \ / \ B C ====> B C \ / | | D D D[копия]
(это очень упрощенная схема)
Пробовал для всех классов прописать DataContract(IsReference = true), так, на всякий случай - не помогло.
Есть ли более "быстрый" способ корректной десериализации, кроме как "ручное" выстраивание структуры повторяющей объект-источник?
Спасибо.


Ответ

При создании сериализатора задайте ему настройку PreserveObjectReferences = true
var settings = new DataContractSerializerSettings { PreserveObjectReferences = true }; var dcs = new DataContractSerializer(typeof(SomeType), settings);
Также можно использовать NetDataContractSerializer

среда, 24 апреля 2019 г.

XML-сериализация по особым правилам

Имеется электронный документооборот. Обмен данными выполняется с помощью XML следующей структуры:

Проблема на лицо - требуется куча классов примерно такой структуры:
[XmlRoot(ElementName="tag1")] public class Tag1 { [XmlAttribute(AttributeName="value")] public int Value { get; set; } }
и потом:
[XmlRoot(ElementName="document")] public class Document { [XmlElement(ElementName="tag1")] public Tag1 Tag1 { get; set; } [XmlElement(ElementName="tag2")] public Tag2 Tag2 { get; set; } [XmlElement(ElementName="tag3")] public Tag3 Tag3 { get; set; } [XmlElement(ElementName="tag4")] public Tag4 Tag4 { get; set; } [XmlElement(ElementName="tag5")] public Tag5 Tag5 { get; set; } [XmlElement(ElementName="tag6")] public Tag6 Tag6 { get; set; } [XmlElement(ElementName="tag7")] public Tag7 Tag7 { get; set; } [XmlElement(ElementName="outerTag1")] public OuterTag1 OuterTag1 { get; set; } [XmlElement(ElementName="outerTag2")] public OuterTag2 OuterTag2 { get; set; } }
Хотелось бы вместо этого написать класс со свойствами простых типов:
[XmlRoot(ElementName="document")] public class Document { [...(ElementName="tag1")] public int Tag1 { get; set; } [...(ElementName="tag2")] public string Tag2 { get; set; } [...(ElementName="tag3")] public DateTime Tag3 { get; set; } [...(ElementName="tag4")] public int Tag4 { get; set; } [...(ElementName="tag5")] public DateTime Tag5 { get; set; } [...(ElementName="tag6")] public string Tag6 { get; set; } [...(ElementName="tag7")] public decimal Tag7 { get; set; } [XmlElement(ElementName="outerTag1")] public OuterTag1 OuterTag1 { get; set; } [XmlElement(ElementName="outerTag2")] public OuterTag2 OuterTag2 { get; set; } }
и не плодить кучу мелких классов типа Tag1, Tag2, ...
Можно ли как-то это сделать? В идеале хотелось бы сделать кастомный атрибут MyXmlElement и использовать его вместо XmlElement, но как научить XmlSerializer понимать его и генерировать соответствующую разметку? Или может есть какой-то другой способ?


Ответ

Отказался от реализации интерфейса IXmlSerializable - решение получалось очень громоздким и не красивым, к тому же нужно учесть много всевозможных нюансов, которые учтены в штатной работе сериализатора.
В итоге написал простой класс:
public class Tag { [XmlAttribute(AttributeName = "value")] public T Value { get; set; }
public override string ToString() => Value.ToString();
public static implicit operator Tag(T value) => new Tag { Value = value }; public static implicit operator T(Tag tag) => tag.Value; }
Это позволило выбросить кучу мелких классов Tag1, Tag2 и т.д. Сам документ принял вид:
[XmlRoot(ElementName="document")] public class Document { [XmlElement(ElementName="tag1")] public Tag Tag1 { get; set; } [XmlElement(ElementName="tag2")] public Tag Tag2 { get; set; } [XmlElement(ElementName="tag3")] public Tag Tag3 { get; set; } [XmlElement(ElementName="tag4")] public Tag Tag4 { get; set; } [XmlElement(ElementName="tag5")] public Tag Tag5 { get; set; } [XmlElement(ElementName="tag6")] public Tag Tag6 { get; set; } [XmlElement(ElementName="tag7")] public Tag Tag7 { get; set; } [XmlElement(ElementName="outerTag1")] public OuterTag1 OuterTag1 { get; set; } [XmlElement(ElementName="outerTag2")] public OuterTag2 OuterTag2 { get; set; } }
Ну и благодаря операторам для неявного приведения типов документ создается так же просто:
var doc = new Document { Tag1 = 1, Tag2 = "text", ... };

четверг, 18 апреля 2019 г.

Сериализация сложного JAVA объекта в JSON

Добрый день подскажите как сериализовать в JSON объект Files? Используя метод toJsonString сериализуются только поле nameDirectory, а поле pool выводится в виде хеша?
import org.json.simple.JSONValue; import org.json.simple.JSONArray; import org.json.simple.JSONObject;
public class Files {
public Map pool;
public String nameDirectory;
public String toJsonString(){ JSONObject JSobj = new JSONObject(pool); JSobj.toJSONString(); } }
public class File { String PathFile; ArrayList Records; boolean PutInEnd; }
public class Record {
private String recordname; private String status; private int countExecution; }


Ответ

Для таких целей есть специальные библиотеки - Jackson и Gson
import java.io.File; import java.io.IOException; import java.util.List; import java.util.ArrayList; import java.util.Map; import java.util.HashMap;
import com.fasterxml.jackson.core.JsonGenerationException; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.JsonMappingException;
class Record { // Поле name и так находится в Record, // называть его recordname - это лишнее private String name; private String status; private int executionCount;
public Record() {}
public Record(String name, String status, int executionCount) { setName(name); setStatus(status); setExecutionCount(executionCount); }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public void setStatus(String status) { this.status = status; }
public String getStatus() { return status; }
public void setExecutionCount(int executionCount) { this.executionCount = executionCount; }
public int getExecutionCount() { return executionCount; } }
// Плохая идея использовать имена классов // входящих в стандартную библиотеку class SomeFile { private String path; // Имена полей должны начинаться со строчной буквы private List records; private boolean putInEnd;
public SomeFile() { records = new ArrayList<>(); }
public SomeFile(String path, boolean putInEnd) { this(); setPath(path); setPutInEnd(putInEnd); }
public void setPath(String path) { this.path = path; }
public String getPath() { return path; }
public void setPutInEnd(boolean putInEnd) { this.putInEnd = putInEnd; }
public void addRecord(Record record) { records.add(record); }
public List getRecords() { return records; } }
class SomeFiles { private String name; private Map pool;
public SomeFiles() { pool = new HashMap<>(); }
public SomeFiles(String name) { this(); setName(name); }
public void setName(String name) { this.name = name; }
public String getName() { return name; }
public void putSomeFile(String name, SomeFile someFile) { pool.put(name, someFile); }
public Map getPool() { return pool; } }
public class App { public static void main(String[] args) { SomeFile f = new SomeFile("/root/secret.txt", false); f.addRecord(new Record("Some Record", "Some status", 42)); f.addRecord(new Record("Another Record", "Same status", 0));
SomeFiles sf = new SomeFiles("Some Files"); sf.putSomeFile("Some File", f);
ObjectMapper mapper = new ObjectMapper(); // Для вывода с отступами mapper.enable(SerializationFeature.INDENT_OUTPUT);
try { // Здесь происходит самая главная магия mapper.writeValue(new File("some_files.json"), sf); } catch(JsonGenerationException exc) { exc.printStackTrace(); } catch(JsonMappingException exc) { exc.printStackTrace(); } catch(IOException exc) { exc.printStackTrace(); } } }
Получится такое:
{ "name" : "Some Files", "pool" : { "Some File" : { "path" : "/root/secret.txt", "records" : [ { "name" : "Some Record", "status" : "Some status", "executionCount" : 42 }, { "name" : "Another Record", "status" : "Same status", "executionCount" : 0 } ] } } }