Member 13348717 Ответов: 2

Сложная 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 к полям и свойствам.

Уровни доступа участников (внутренний, частный, защищенный или публичный) никоим образом не влияют на контракт данных."

Однако если ключи должны быть защищены, то, имхо, вы должны планировать использование шифрования.

2 Ответов

Рейтинг:
2

BillWoodruff

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

В этом примере сериализация происходит дважды: во-первых, для сохранения экземпляра ' Class1 без его список экземпляров ' Class2, но, включая его список GUID экземпляра ' Class2; и, во-вторых, сохранить список экземпляров 'Class2 в 'Class1.

Во-первых, вот как может выглядеть использование кода:

private void button2_Click(object sender, EventArgs e)
{
    // make a 'Class1 instance
    Class1  class1 = new Class1();

    // add some 'Class2 instances
    class1.GetNewClass2("c2a");
    class1.GetNewClass2("c2b");
    class1.GetNewClass2("c2c");

    // serialize the 'Class1 instance
    SerializationManager.Serialize<Class1>(class1, "test1", false);

    // deserialize the 'Class1 instance from file
    Class1 c1resurrected = SerializationManager.DeSerialize<Class1>("test1", false);

    // serialize the list of 'Class2 instances in 'Class1
    SerializationManager.Serialize<List<Class2>>(class1.Class2s, "test2", false);

    // deserialize the list of 'Class2 instances from file
    List<Class2> c2Listresurrected = SerializationManager.DeSerialize<List<Class2>>("test2", false);

    // set the list of 'Class2 instances in the restored
    // 'Class1 instance to the resored list of 'Class2 instances
    c1resurrected.Class2s = c2Listresurrected;
}


using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Runtime.Serialization;

namespace DataContractDemo
{
    [DataContract]
    public class Class1
    {
        [DataMember]
        public List<Guid> Class2IDs { get; set; }

        [NonSerialized] public List<Class2> Class2s;

        [NonSerialized] private static int c2Counter = 0;

        public Class1()
        {
            Class2IDs = new List<Guid>();
            Class2s = new List<Class2>();
        }

        public Class2 GetNewClass2(string name)
        {
            var newClass2 = new Class2(Guid.NewGuid(), $"C2_{c2Counter++}");

            Class2IDs.Add(newClass2.ID);

            Class2s.Add(newClass2);

            return newClass2;
        }
    }

    [DataContract]
    public class Class2
    {
        [DataMember]
        public Guid ID { get; set; }

        [DataMember]
        public string Name { get; set; }

        public Class2(Guid id, string name)
        {
            ID = id;
            Name = name;
        }
    }

    public static class SerializationManager
    {
        private static string baseFolderPath = @"C:\Users\YourUserName\Desktop\TestDCSerialization\";

        private static DataContractSerializer dcs;

        public static void Serialize<T>(T instance, string filename1 = "", bool usegzip = true)
        {
            dcs = new DataContractSerializer(typeof(T));

            filename1 = baseFolderPath + filename1 + (usegzip ? @".gz" : @".xml");

            if (File.Exists(filename1)) File.Delete(filename1);

            // gzip writer
            if (usegzip)
            {
                using (var compressedFileStream = File.Create(filename1))
                {
                    using (var compressionStream =
                        new GZipStream(compressedFileStream, CompressionLevel.Optimal, true))
                    {
                        dcs.WriteObject(compressionStream, instance);
                        compressionStream.Close();
                    }
                }
            }
            else
            {
                using (var writer = new FileStream(filename1, FileMode.OpenOrCreate, FileAccess.Write))
                {
                    dcs.WriteObject(writer, instance);
                }
            }
        }

        public static T DeSerialize<T>(string filename1 = "", bool usegzip = true)
        {
            dcs = new DataContractSerializer(typeof(T));

            filename1 = baseFolderPath + filename1 + (usegzip ? @".gz" : @".xml");

            if (!File.Exists(filename1))
            {
                throw new FileNotFoundException($"{filename1} does not exist");
            }

            T result;

            if (usegzip)
            {
                using (var xmlStream = new MemoryStream())
                {
                    using (var fs = File.Open(filename1, FileMode.Open))
                    {
                        using (var deCompStream =
                            new GZipStream(fs, CompressionMode.Decompress, true))
                        {
                            deCompStream.CopyTo(xmlStream);
                            deCompStream.Close(); // using 'Close here fixes a common problem with WCF
                            // must reset to beginning of stream
                            xmlStream.Position = 0;
                        }

                        result = (T) dcs.ReadObject(xmlStream);

                        xmlStream.Close();
                    }
                }
            }
            else
            {
                var reader = new FileStream(filename1, FileMode.Open, FileAccess.Read);
                result = (T) dcs.ReadObject(reader);
            }

            return result;
        }
    }
}


Member 13348717

Спасибо Билл,

Если я чего-то не упускаю, то этот подход создает проблему с синхронизацией идентификатора и списка объектов

Оба списка должны быть открыты для доступа к спискам участников. свойство List для доступа к фактическим данным и свойство List для сериализации списков идентификаторов. Хотя может существовать отдельный общедоступный метод, который добавляет новые или существующие объекты Class2 в качестве членов списка в Class1, нет ничего, что могло бы помешать разработчикам (намеренно или по ошибке) добавлять элементы в один из этих списков независимо, которые затем будут генерировать списки (и последующая сериализация) не синхронизируется. Я попытался изначально добавить код для синхронизации списка идентификаторов со списком объектов и пометить его атрибутом [OnSerialized], но он так и не сработал. Я мог бы также разместить код в методе Serialize для синхронизации этих свойств, но в реальном приложении (а не только в приведенном выше примере) это включает в себя множество вложенных циклов итераций для достижения этой синхронизации.

Конечно, затем необходимо выполнить вложенные итерации для повторной синхронизации списков объектов с десериализованными списками идентификаторов после десериализации XML.

BillWoodruff

Я считаю (потому что я реализовал такой код в прошлом), что точки, которые вы поднимаете ... о контроле доступа ... легко обрабатываются с помощью соответствующих объявлений методов доступа для различных объектов класса и/или с помощью вложенных/или / запечатанных классов и т. д.

Это ваше намерение (Я думаю) отделить экземпляры от их идентификаторов. Если вы не хотите иметь доступа к экземплярам Class2, объявите Class2 как "внутренний", вложите его в Class1. Затем обрабатывайте все взаимодействия с помощью открытых методов в классе 1.

твое здоровье, Билл

Member 13348717

Мне нужен доступ к экземплярам класса 2. Мне действительно нужен доступ к членам ID только для целей сериализации. Проблема в том, что свойства ID должны быть общедоступными для сериализации, то есть я должен иметь как экземпляр, так и отдельное свойство, объявленное как общедоступное.

Member 13348717

Я принял первое решение XSLT, так как мне понравилась идея сохранить перевод отдельно от кода. Не знаю почему, но мне это показалось более элегантным решением, чем необходимость выставлять больше свойств, чем мне действительно нужно. Теперь наступает трудная часть его реальной реализации и адаптации по всему спектру моих классов :)

BillWoodruff

Я рад, что у вас есть несколько полезных идей !

твое здоровье, Билл

Рейтинг:
15

Graeme_Grant

Этот вопрос потребовал небольшого исследования, однако решение очень простое - используйте XSLT для преобразования полных XML-данных в сокращенный или подмножество XML-данных. Вот 3 ссылки, которые помогли с решением:

1. c# - как преобразовать XML в строку без использования файлов в .NET? - переполнение стека[^]
2. Преобразование XML-файла в другой XML-файл с помощью XSLT-Stack Overflow[^]
3. XSLT-преобразование[^]

Был использован следующий вспомогательный класс XML-конвертера:

using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace XmlJsonSubsetProperties
{
    public static class XmlConverter
    {
        public static string FromClass<T>(T data, XmlSerializerNamespaces ns = null)
        {
            string response = string.Empty;

            var ms = new MemoryStream();
            try
            {
                ms = FromClassToStream(data, ns);

                if (ms != null)
                {
                    ms.Position = 0;
                    using (var sr = new StreamReader(ms))
                        response = sr.ReadToEnd();
                }
            }
            finally
            {
                // don't want memory leaks...
                ms.Flush();
                ms.Dispose();
                ms = null;
            }

            return response;
        }

        public static MemoryStream FromClassToStream<T>(T data, XmlSerializerNamespaces ns = null)
        {
            var stream = default(MemoryStream);

            if (data != null)
            {
                var settings = new XmlWriterSettings()
                {
                    Encoding = Encoding.UTF8,
                    Indent = true,
                    ConformanceLevel = ConformanceLevel.Auto,
                    CheckCharacters = true,
                    OmitXmlDeclaration = false
                };

                try
                {
                    //XmlSerializer ser = new XmlSerializer(typeof(T));
                    XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(T));

                    stream = new MemoryStream();
                    using (XmlWriter writer = XmlWriter.Create(stream, settings))
                    {
                        serializer.Serialize(writer, data, ns);
                        writer.Flush();
                    }
                    stream.Position = 0;
                }
                catch (Exception ex)
                {
                    stream = default(MemoryStream);
#if DEBUG
                    Debug.WriteLine(ex);
                    Debugger.Break();
#endif
                }
            }
            return stream;
        }

        public static T ToClass<T>(string data)
        {
            var response = default(T);

            if (!string.IsNullOrEmpty(data))
            {
                var settings = new XmlReaderSettings() { IgnoreWhitespace = true };

                try
                {
                    XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(T));
                    XmlReader reader = XmlReader.Create(new StringReader(data), settings);
                    response = (T)Convert.ChangeType(serializer.Deserialize(reader), typeof(T));
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex);
                    Debugger.Break();
                }
            }
            return response;
        }
    }
}

Наряду с этим в C# - XmlSerializer не давая исключение filenotfoundexception в конструкторе исправить[^]:
using System;
using System.Collections.Generic;
using System.Xml.Serialization;

namespace XmlJsonSubsetProperties
{
    public static class XmlSerializerFactoryNoThrow
    {
        public static Dictionary<Type, XmlSerializer> cache = new Dictionary<Type, XmlSerializer>();

        private static readonly object SyncRootCache = new object();

        public static XmlSerializer Create(Type type)
        {
            XmlSerializer serializer;

            lock (SyncRootCache)
                if (cache.TryGetValue(type, out serializer))
                    return serializer;

            //multiple variables of type of one type are the same instance
            lock (type)
                //constructor XmlSerializer.FromTypes does not throw the first chance exception           
                serializer = XmlSerializer.FromTypes(new[] { type })[0];

            lock (SyncRootCache) cache[type] = serializer;
            return serializer;
        }
    }
}

И, наконец, тестовое решение:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;

namespace XmlJsonSubsetProperties
{
    internal static class Program
    {
        private static void Main()
        {
            var plasticItems = new Category() { ID = "ALLPLAS", Name = "All Plastic Items" };
            var otherItems = new Category() { ID = "NONPLAS", Name = "Non Plastic" };

            var fullList = new CategoryList()
            {
                Categories = new List<Category>()
                {
                    plasticItems,
                    otherItems
                }
            };

            // input-xml
            string xmlinput = XmlConverter.FromClass(fullList);

            // xslt
            var lines = xmlinput.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
            string header = lines[1];
            string footer = lines[lines.Length - 1];
            const string idFieldName = nameof(Category.ID);
            const string catFieldName = nameof(CategoryList.Categories);
            string xsltinput = $"<?xml version=\"1.0\" encoding=\"iso-8859-1\"?><xsl:stylesheet version=\"1.0\" xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"><xsl:template match=\"/\">{header}<xsl:for-each select=\"{nameof(CategoryList)}/{catFieldName}\"><{catFieldName}><{idFieldName}><xsl:value-of select=\"{idFieldName}\"/></{idFieldName}></{catFieldName}></xsl:for-each>{footer}</xsl:template></xsl:stylesheet>";

            // output-xml
            string xmloutput = Transform(xmlinput, xsltinput);

            var xmlIdList = XmlConverter.ToClass<CategoryList>(xmloutput);
            Debugger.Break();
        }

        private static string Transform(string xmlinput, string xsltinput)
        {
            string xmloutput;
            // Prepare input-xml
            var doc = new XPathDocument(new StringReader(xmlinput));

            // Prepare XSLT
            var xslt = new XslTransform();
            // Creates a XmlReader from your xsl string
            using (XmlReader xmlreader = XmlReader.Create(new StringReader(xsltinput)))
            {
                //Load the stylesheet.
                xslt.Load(xmlreader);

                // transform
                using (var sw = new StringWriter())
                {
                    xslt.Transform(doc, null, sw);

                    // save to string
                    xmloutput = sw.ToString();
                }
            }

            return xmloutput;
        }
    }

    public class Category
    {
        public string ID { get; set; }
        public string Name { get; set; }
    }

    [XmlRoot(nameof(CategoryList))]
    public class CategoryList
    {
        [XmlElement]
        public List<Category> Categories { get; set; }
    }
}

Это полный набор данных XML:
<?xml version="1.0" encoding="utf-8"?>
<CategoryList 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>
  </Categories>
  <Categories>
    <ID>NONPLAS</ID>
    <Name>Non Plastic</Name>
  </Categories>
</CategoryList>

XSLT-преобразование:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">
    <xsl:for-each select="CategoryList/Categories">
    <Categories>
	  <ID><xsl:value-of select="ID"/></ID>
    </Categories>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

И выход подмножества:
<?xml version="1.0" encoding="utf-16"?>
<CategoryList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Categories>
    <ID>ALLPLAS</ID>
  


BillWoodruff

+5 хороших вещей !

Graeme_Grant

Спасибо. Мне нравятся такие вопросы/проблемы... самое трудное-найти простое решение...

Я знаю, что вы попросили меня написать статью о JSON, и это уже происходит. Я мог бы проследить за этим с помощью XML-файла... ;)

BillWoodruff

Я также получаю удовольствие от подобных проблем; я учусь чему-то, перебирая то, что возможно.

С тех пор как я обнаружил средства DataContract WCF (см. Пример кода ниже), я никогда не "оглядывался" на JSON, но я понимаю, что он играет определенную роль. Используя GZip (см. Ниже), вы получаете невероятное сжатие безумно подробного XML :)

Graeme_Grant

Это очень интересный способ. :)

Я все еще предпочитаю JSON XML...: P

BillWoodruff

Грэм, обязательно ознакомьтесь с работой FastJSON Мехди Голама с сериализацией здесь, на CP.

https://www.codeproject.com/Articles/345070/fastBinaryJSON

https://www.codeproject.com/Articles/345070/fastBinaryJSON

твое здоровье, Билл

Graeme_Grant

Спасибо... У меня есть но все еще предпочитаю Newtownsoft ;)

BillWoodruff

Будьте уверены, у меня нет никакого желания "обращать" кого бы то ни было из того, что они предпочитают ... во что-то другое :)

Graeme_Grant

Я понял, откуда вы пришли. :)

Как только я закончу и опубликую статью, станет ясно, почему.

BillWoodruff

Знаете ли вы, что WCF предоставляет средства JSON через DataContractJsonSerializer ?

Graeme_Grant

Да, на самом деле это так. Я использовал их, но нашел их слишком ограничивающими. ;)

Member 13348717

Спасибо Грэму за подробный ответ и ссылки. Сейчас я перебираю его, чтобы попытаться адаптировать к требованиям.

Вопрос у меня заключается с участником материального объекта недвижимости, не корневой класс Categoty. Когда объекты категории сериализуются, категории[x].Элементы [y].элементы списка материалов, которые сериализуются, - это те, которые мне нужны только для идентификатора, а не для полного объекта класса материалов.

Я отредактировал исходный вопрос, чтобы показать XML, который я получал с исходным кодом, а также то, чего я пытаюсь достичь. Я пытаюсь адаптировать ваше решение от работы с корневым объектом (категорией) к свойству члена в элементе списка ( категория[x].Items[y]. Material), но не совсем уверен, как сделать так, чтобы это работало на члене, а не на корневом классе.

Graeme_Grant

Конечно, принцип тот же, XSLT будет описывать, как выводить данные. Я прошел только один слой.

Все, что вам нужно сделать, это добавить структуру класса, макет данных, обновить XSLT и запустить. :)

Graeme_Grant

Я сделал для вас XSLT-отображение... довольно простое. ;)

Вам нужно будет обновить решение с помощью ваших классов и XSLT.

Member 13348717

Было интересно следить за этим разговором. Моим первоначальным требованием была просто сериализация, и я выбрал XML, потому что это казалось самым простым, но у меня также будет потребность в JSON ( я думаю, что по мере того, как проект будет продвигаться вперед с требованием веб-данных ). Все это, конечно, означает, что мне нужно гораздо больше читать

Graeme_Grant

Добро пожаловать. Я добавлю ваш сценарий в статью, которую пишу о работе с данными JSON. Я оставлю здесь заметку, как только она будет опубликована. :)