Graeme_Grant
Это подробный ответ, который охватывает ряд моментов как в вашем вопросе, так и не в нем, однако вы можете столкнуться с ними в будущем:
* Элементы против атрибутов
* Имена элементов/атрибутов, отличные от имен свойств
* Пользовательский элемент &амп; атрибут сериализации: перечисления &амп; разделы CDATA видах
* Дополнительный узел сериализации
* Как избежать исключения сериализации "файл не найден"
Я включил некоторые комментарии, которые, надеюсь, помогут объяснить, как и почему.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace WorkingWithXml
{
class Program
{
static void Main(string[] args)
{
var data = new XmlRootClass
{
Id = 12345567890123456789,
Title = "Widget",
Amount = new AmountType { Value = 123.45, CurrencyID = CurrencyCodeType.USD },
Description = new CData("This is a description with embedded html"),
};
var raw = XmlConverter.FromClass(data);
var newData = XmlConverter.ToClass<XmlRootClass>(raw);
Debugger.Break();
}
}
// Naming a root element
[XmlRoot(ElementName = "Product", IsNullable = false)]
public class XmlRootClass
{
[XmlElement("id123)")]
public ulong Id { get; set; }
public string Title { get; set; }
// a value type element with an attribute
[XmlElement("amt")]
public AmountType Amount { get; set; }
// Custom element data format
[XmlElement("description", typeof(CData))]
public CData Description { get; set; }
// example of optional serialization
[XmlElement, DefaultValue(false)]
public bool IsAvailable { get; set; }
}
public class AmountType
{
[XmlText]
public double Value { get; set; }
//enum type attribute (same works with an XmlElement)
[XmlAttribute("currencyID")]
public string CurrencyID_intern { get; set; }
[XmlIgnore]
public CurrencyCodeType? CurrencyID
{
get
{
return CurrencyID_intern.StringToEnum<CurrencyCodeType?>
(CurrencyCodeType.CustomCode, isNullable: true);
}
set { CurrencyID_intern = value.ToString(); }
}
}
public enum CurrencyCodeType
{
CustomCode, // missing from list
AUD, // Australia dollar
JPY, // Japanese yen
USD, // US dollar
}
public static class EnumExtensions
{
public static T StringToEnum<T>(this string input, T defaultValue = default(T), bool isNullable = false)
{
T outType;
if (string.IsNullOrEmpty(input) &&
isNullable &&
Nullable.GetUnderlyingType(typeof(T)) != null &&
Nullable.GetUnderlyingType(typeof(T)).GetElementType() == null)
return default(T);
return input.EnumTryParse(out outType) ? outType : defaultValue;
}
public static bool EnumTryParse<T>(this string input, out T theEnum)
{
Type type = Nullable.GetUnderlyingType(typeof(T)) != null ? Nullable.GetUnderlyingType(typeof(T)) : typeof(T);
foreach (string en in Enum.GetNames(type))
if (en.Equals(input, StringComparison.CurrentCultureIgnoreCase))
{
theEnum = (T)Enum.Parse(type, input, true);
return true;
}
theEnum = default(T);
return false;
}
}
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 (!EqualityComparer<T>.Default.Equals(data, default(T)))
{
var settings = new XmlWriterSettings()
{
Encoding = Encoding.UTF8,
Indent = true,
ConformanceLevel = ConformanceLevel.Auto,
CheckCharacters = true,
OmitXmlDeclaration = false
};
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;
}
return stream;
}
public static T ToClass<T>(string data)
{
var response = default(T);
if (!string.IsNullOrEmpty(data))
{
var settings = new XmlReaderSettings() { IgnoreWhitespace = true };
XmlSerializer serializer = XmlSerializerFactoryNoThrow.Create(typeof(T));
XmlReader reader = XmlReader.Create(new StringReader(data), settings);
response = (T)Convert.ChangeType(serializer.Deserialize(reader), typeof(T));
}
return response;
}
}
// ref: http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor/39642834#39642834
public static class XmlSerializerFactoryNoThrow
{
public static Dictionary<Type, XmlSerializer> cache = new Dictionary<Type, XmlSerializer>();
private static object SyncRootCache = new object();
public static XmlSerializer Create(Type type)
{
XmlSerializer serializer;
lock (SyncRootCache)
if (cache.TryGetValue(type, out serializer))
return serializer;
lock (type) //multiple variable of type of one type is same instance
{
//constructor XmlSerializer.FromTypes does not throw the first chance exception
serializer = XmlSerializer.FromTypes(new[] { type })[0];
}
lock (SyncRootCache) cache[type] = serializer;
return serializer;
}
}
// ref: http://codercorner.blogspot.com.au/2006/11/serialization-and-cdata.html
public class CData : IXmlSerializable
{
public CData()
{ }
public CData(string text)
{
this.text = text;
}
private string text;
public string Text => text;
XmlSchema IXmlSerializable.GetSchema() => null;
void IXmlSerializable.ReadXml(XmlReader reader)
{
text = reader.ReadElementContentAsString();
reader.Read(); // without this line, you will lose value of all other fields
}
void IXmlSerializable.WriteXml(XmlWriter writer)
{
writer.WriteCData(text);
}
public override string ToString() => text;
}
}
Запустите новое консольное приложение, вставьте его и запустите, чтобы посмотреть, что оно делает.
Надеюсь, это поможет! :)