Сложная XML-сериализация .. Как реализовать частичную сериализацию класса для списка объектов
были в многочисленных статьях о сериализации, пытаясь понять это, но ни к чему не придя. Приведенный ниже код простой, но он немного длиннее, чем я бы хотел опубликовать, но я попытался свести его к минимуму, все еще объясняя мою проблему. Проще говоря: я хочу сериализовать класс, содержащий список другого класса, но я хочу сериализовать только свойство общедоступного идентификатора для всех объектов в списке. Что усложняет, так это то, что класс, который находится в списке, должен иметь возможность полностью сериализовать себя отдельно (то есть в отдельном XML-файле), что означает, что я не могу просто пометить все свойства без идентификатора атрибутом XmlIgnore.
Что я уже пробовал:
Я пробовал использовать атрибуты OnSerializing и On DeSerialized, но по какой-то причине они никогда не срабатывают. В приведенном ниже коде материал класса хранится в отдельном XML-документе, который считывается в приложение при запуске и становится глобально доступным через приложение. Существует отдельный XML-документ, содержащий сериализованный класс элементов. При десериализации этих объектов я хочу только десериализовать материал.Свойство ID, а затем найдите соответствующий объект в моем глобально доступном списке.
На данный момент мне все равно,является ли это XML или JSON на данный момент (XML просто казался проще). В конце концов мне понадобится JSON, но прямо сейчас мне просто нужно что-то сделать для тестирования и прототипирования, прежде чем переходить к более надежному сериализатору.
using System; using System.Collections.Generic; using System.IO; using System.Runtime.Serialization; using System.Windows.Forms; using System.Xml.Serialization; namespace SerializationTestNET { static class Program { /// <summary> /// The main entry point for the application. /// </summary> [STAThread] static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); GlobalData.InitDefaultData(); Category PlasticItems = new Category() { ID = "ALLPLAS", Name = "All Plastic Items" }; Category OtherItems = new Category() { ID = "NONPLAS", Name = "Non Plastic" }; Item PlasticAndSteel = new Item() { ID = "PLASSTEL", Name = "Plastic and Steel", Category = PlasticItems }; PlasticAndSteel.Materials.Add(GlobalData.Plastic); PlasticAndSteel.Materials.Add(GlobalData.Steel); Item PlasticAndRubber = new Item() { ID = "PLASRUBR", Name = "Plastic and Rubber", Category = PlasticItems }; PlasticAndRubber.Materials.Add(GlobalData.Plastic); PlasticAndRubber.Materials.Add(GlobalData.Rubber); Item Steel = new Item() { ID = "STEL", Name = "Steel", Category = OtherItems }; Steel.Materials.Add(GlobalData.Steel); Item Rubber = new Item() { ID = "RUBR", Name = "Rubber", Category = OtherItems }; Rubber.Materials.Add(GlobalData.Rubber); PlasticItems.Items.Add(PlasticAndSteel); PlasticItems.Items.Add(PlasticAndRubber); OtherItems.Items.Add(Steel); OtherItems.Items.Add(Rubber); MaterialDocument materialDocument = new MaterialDocument() { Materials = GlobalData.AllMaterials }; using (StreamWriter writer = new StreamWriter("C:\\Temp\\Materials.xml")) materialDocument.Serialize(writer); MaterialDocument materialsDeSerializedDocument; using (StreamReader reader = new StreamReader("C:\\Temp\\Materials.xml")) materialsDeSerializedDocument = MaterialDocument.DeSerialize(reader); CategoryDocument categoriesDocument = new CategoryDocument(); categoriesDocument.Categories.Add(PlasticItems); categoriesDocument.Categories.Add(OtherItems); using (StreamWriter writer = new StreamWriter("C:\\Temp\\Categories.xml")) categoriesDocument.Serialize(writer); CategoryDocument categoriesDocumentDeSerializeDocument; using (StreamReader reader = new StreamReader("C:\\Temp\\Categories.xml")) categoriesDocumentDeSerializeDocument = CategoryDocument.DeSerialize(reader); Application.Run(new Form1()); } } public static class GlobalData { public static List<Material> AllMaterials { get; set; } = new List<Material>(); // Globally available data that contains both static default data as well as user data added in at a later stage [XmlIgnore] public static List<Item> AvailableItems { get; set; } = new List<Item>(); // A list of all available items loaded in the currnet running application instance [XmlIgnore] public static Material Plastic { get; set; } public static Material Steel { get; set; } public static Material Rubber { get; set; } // Static default material data that is referenced through the application public static Material GetMaterial(string id) // Locate and return a material by ID { foreach (Material material in AllMaterials) if (material.ID == id) return material; return null; } public static void InitDefaultData() { AllMaterials.Add(Plastic = new Material() { ID = "PLAS", Name = "Plastic", MaterialValue = 5 }); AllMaterials.Add(Steel = new Material() { ID = "STEL", Name = "Steel", MaterialValue = 5 }); AllMaterials.Add(Rubber = new Material() { ID = "RUBR", Name = "Rubber", MaterialValue = 7 }); } } public class BaseClass // Base class for all application objects requiring an ID and Name property { public string ID { get; set; } public string Name { get; set; } } public class Material : BaseClass { public decimal MaterialValue; } public class Item : BaseClass { public List<Material> Materials { get; set; } = new List<Material>(); [XmlIgnore] public Category Category { get; set; } public List<string> MaterialIDs { get; set; } = new List<String>(); public string CategoryID { get; set; } [OnSerializing] public void CreateIDList() { MaterialIDs.Clear(); // Clear the list and recreate it tomake it current with all Materials in the list. foreach (Material material in Materials) MaterialIDs.Add(material.ID); CategoryID = Category.ID; // Make sure we have the current Category ID to serialize } [OnDeserialized] public void EstablishLinks() { foreach (string id in MaterialIDs) // Loop through all ids that were derserialized and link them back to their correct material objects. Materials.Add(GlobalData.GetMaterial(id)); } } public class Category : BaseClass { public List<Item> Items { get; set; } = new List<Item>(); public string CategoryNotes { get; set; } } public class CategoryDocument // Class storing lists of categories for use within the application and for serialization and deserilation of this data. { public List<Category> Categories { get; set; } = new List<Category>(); public string Serialize(StreamWriter writer) { var serializer = new XmlSerializer((typeof(CategoryDocument))); serializer.Serialize(writer, this); return writer.ToString(); } public static CategoryDocument DeSerialize(StreamReader reader) { var serializer = new XmlSerializer(typeof(CategoryDocument)); return serializer.Deserialize(reader) as CategoryDocument; } } public class MaterialDocument // Class storing lists of material items { public List<Material> Materials { get; set; } = new List<Material>(); public string Serialize(StreamWriter writer) { var serializer = new XmlSerializer((typeof(MaterialDocument))); serializer.Serialize(writer, this); return writer.ToString(); } public static MaterialDocument DeSerialize(StreamReader reader) { var serializer = new XmlSerializer(typeof(MaterialDocument)); return serializer.Deserialize(reader) as MaterialDocument; } } }
Текущие категории XML файл :
<?xml version="1.0" encoding="utf-8"?> <CategoryDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Categories> <ID>ALLPLAS</ID> <Name>All Plastic Items</Name> <Items> <Item> <ID>PLASSTEL</ID> <Name>Plastic and Steel</Name> <Materials> <Material> <ID>PLAS</ID> <Name>Plastic</Name> <MaterialValue>5</MaterialValue> </Material> <Material> <ID>STEL</ID> <Name>Steel</Name> <MaterialValue>5</MaterialValue> </Material> </Materials> <MaterialIDs /> </Item> <Item> <ID>PLASRUBR</ID> <Name>Plastic and Rubber</Name> <Materials> <Material> <ID>PLAS</ID> <Name>Plastic</Name> <MaterialValue>5</MaterialValue> </Material> <Material> <ID>RUBR</ID> <Name>Rubber</Name> <MaterialValue>7</MaterialValue> </Material> </Materials> <MaterialIDs /> </Item> </Items> </Categories> <Categories> <ID>NONPLAS</ID> <Name>Non Plastic</Name> <Items> <Item> <ID>STEL</ID> <Name>Steel</Name> <Materials> <Material> <ID>STEL</ID> <Name>Steel</Name> <MaterialValue>5</MaterialValue> </Material> </Materials> <MaterialIDs /> </Item> <Item> <ID>RUBR</ID> <Name>Rubber</Name> <Materials> <Material> <ID>RUBR</ID> <Name>Rubber</Name> <MaterialValue>7</MaterialValue> </Material> </Materials> <MaterialIDs /> </Item> </Items> </Categories> </CategoryDocument>
Нужные категории XML файл :
<?xml version="1.0" encoding="utf-8"?> <CategoryDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Categories> <ID>ALLPLAS</ID> <Name>All Plastic Items</Name> <Items> <Item> <ID>PLASSTEL</ID> <Name>Plastic and Steel</Name> <Materials> <Material> <ID>PLAS</ID> </Material> <Material> <ID>STEL</ID> </Material> </Materials> <MaterialIDs /> </Item> <Item> <ID>PLASRUBR</ID> <Name>Plastic and Rubber</Name> <Materials> <Material> <ID>PLAS</ID> </Material> <Material> <ID>RUBR</ID> </Material> </Materials> <MaterialIDs /> </Item> </Items> </Categories> <Categories> <ID>NONPLAS</ID> <Name>Non Plastic</Name> <Items> <Item> <ID>STEL</ID> <Name>Steel</Name> <Materials> <Material> <ID>STEL</ID> </Material> </Materials> <MaterialIDs /> </Item> <Item> <ID>RUBR</ID> <Name>Rubber</Name> <Materials> <Material> <ID>RUBR</ID> </Material> </Materials> </Item> </Items> </Categories> </CategoryDocument>
Выходной файл XML материалов :
<?xml version="1.0" encoding="utf-8"?> <MaterialDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Materials> <Material> <ID>PLAS</ID> <Name>Plastic</Name> <MaterialValue>5</MaterialValue> </Material> <Material> <ID>STEL</ID> <Name>Steel</Name> <MaterialValue>5</MaterialValue> </Material> <Material> <ID>RUBR</ID> <Name>Rubber</Name> <MaterialValue>7</MaterialValue> </Material> </Materials> </MaterialDocument>
BillWoodruff
+5 за интересный и тщательно написанный вопрос.
BillWoodruff
Мне любопытно спросить вас, в чем причина" двойной " сериализации здесь.
Также: почему вы выбрали формат JSON ?
BillWoodruff
Член 13348717 написал (в комментарии ниже) : "проблема в том, что свойства идентификатора должны быть общедоступными для сериализации"
Это не относится к WCF/DataContract:
https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/using-data-contracts
"Вы можете применить атрибут DataMemberAttribute к полям и свойствам.
Уровни доступа участников (внутренний, частный, защищенный или публичный) никоим образом не влияют на контракт данных."
Однако если ключи должны быть защищены, то, имхо, вы должны планировать использование шифрования.