This question required a little research, however, the solution is a simple one - Use an XSLT to transform the full XML data into an abbreviated or a subset XML dataset. Here are the 3 links that helped with the solution:
1.
c# - How to transform XML as a string w/o using files in .NET? - Stack Overflow[
^]
2.
Converting XML file to another XML file using XSLT - Stack Overflow[
^]
3.
XSLT Transformation[
^]
The following XML Converter Helper class was used:
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
{
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 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;
}
}
}
Along with this
c# - XmlSerializer giving FileNotFoundException at constructor fix[
^]:
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;
lock (type)
serializer = XmlSerializer.FromTypes(new[] { type })[0];
lock (SyncRootCache) cache[type] = serializer;
return serializer;
}
}
}
And lastly, the test solution:
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
}
};
string xmlinput = XmlConverter.FromClass(fullList);
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>";
string xmloutput = Transform(xmlinput, xsltinput);
var xmlIdList = XmlConverter.ToClass<CategoryList>(xmloutput);
Debugger.Break();
}
private static string Transform(string xmlinput, string xsltinput)
{
string xmloutput;
var doc = new XPathDocument(new StringReader(xmlinput));
var xslt = new XslTransform();
using (XmlReader xmlreader = XmlReader.Create(new StringReader(xsltinput)))
{
xslt.Load(xmlreader);
using (var sw = new StringWriter())
{
xslt.Transform(doc, null, sw);
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; }
}
}
This is the Full XML dataset:
="1.0"="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>
The XSLT transformation:
="1.0"="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>
And the subset output:
="1.0"="utf-16"
<CategoryList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Categories>
<ID>ALLPLAS</ID>
</Categories>
<Categories>
<ID>NONPLAS</ID>
</Categories>
</CategoryList>
UPDATE: The following XSLT will transform your original CategoryDocument to the "ID" version as per your updated question:
="1.0"="iso-8859-1"
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<CategoryDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:for-each select="CategoryDocument/Categories">
<Categories>
<ID><xsl:value-of select="ID"/></ID>
<Name><xsl:value-of select="Name"/></Name>
<Items>
<xsl:for-each select="Items/Item">
<Item>
<ID><xsl:value-of select="ID"/></ID>
<Name><xsl:value-of select="Name"/></Name>
<Materials>
<xsl:for-each select="Materials/Material">
<Material>
<ID><xsl:value-of select="ID"/></ID>
</Material>
</xsl:for-each>
</Materials>
</Item>
</xsl:for-each>
</Items>
</Categories>
</xsl:for-each>
</CategoryDocument>
</xsl:template>
</xsl:stylesheet>
NOTE: This is an updated XSLT only. You will need to update the above code with your classes and this XSLT.
Enjoy! :)