我正在寻找一种将对象树转换为XML的方法.写作会很有趣,但我相信有人已经写过了.这是我的愿望清单:
它不应该关心构造函数
它理想情况下应该处理循环引用(不太f)
它不应该要求更改对象 - 例如,没有自定义属性
它不应该关心或需要已知的类型(例如,XmlInclude)
XML应该简单易懂 - 操作团队成员需要人类可读
如果属性无法序列化,则应该只是抑制错误并继续
可以处理列表和词典
我不需要重建对象模型,因此只写解决方案很好(可能是预期的).
我认为折扣:
XmlSerializer - 需要无参数构造函数,没有循环引用支持
DataContractSerializer - 需要属性(选择加入)
Paul Stovell.. 6
罗伯特罗斯尼的帖子让我觉得这可能比我想象的要少.所以这是一次非常艰难的尝试.它处理以下内容:
如果它无法读取属性,则会将异常打印为值
循环引用和多次出现.它将ID与每个元素相关联; 如果一个元素出现两次,它只是指向ref ID.Ref ID对象图是唯一的(我应该使用GUID,但这符合我的目的).
派生类型没有问题
它不需要属性或特定构造函数或其他废话
它可以处理只读属性
这是输出的一个例子(在我的测试对象中,Order上的"Currency"产品抛出异常).
Paul Stovell Paul Stovell Apples 27.30 Something bad happened Pears 17.85 Something bad happened
这是示例对象模型和用法:
static void Main(string[] args) { var customer = new Customer(); customer.FirstName = "Paul"; customer.LastName = "Stovell"; customer.Orders.Add(new Order(customer) { Price = 27.30M, SKU = "Apples"}); customer.Orders.Add(new Order(customer) { Price = 17.85M, SKU = "Pears"}); customer.Orders.Add(customer.Orders[0]); var output = new StringWriter(); var writer = new XmlTextWriter(output); writer.Formatting = Formatting.Indented; WriteComplexObject("Customer", customer, writer); Console.WriteLine(output.ToString()); Console.ReadKey(); } class Customer { private readonly List_orders = new List (); public Customer() { } public string FirstName { get; set; } public string LastName { get; set; } public string FullName { // Read-only property test get { return FirstName + " " + LastName; } } public List Orders { // Collections test get { return _orders; } } } class Order { private readonly Customer _customer; public Order(Customer customer) { _customer = customer; } public string SKU { get; set; } public decimal Price { get; set; } public string Currency { // A proprty that, for some reason, can't be read get { throw new Exception("Something bad happened"); } } public Customer Customer { get { return _customer; } } }
这是实施:
public static void WriteObject(string name, object target, XmlWriter writer) { WriteObject(name, target, writer, new List
我仍然有兴趣知道是否有更多经过试验和测试的解决方案.
罗伯特罗斯尼的帖子让我觉得这可能比我想象的要少.所以这是一次非常艰难的尝试.它处理以下内容:
如果它无法读取属性,则会将异常打印为值
循环引用和多次出现.它将ID与每个元素相关联; 如果一个元素出现两次,它只是指向ref ID.Ref ID对象图是唯一的(我应该使用GUID,但这符合我的目的).
派生类型没有问题
它不需要属性或特定构造函数或其他废话
它可以处理只读属性
这是输出的一个例子(在我的测试对象中,Order上的"Currency"产品抛出异常).
Paul Stovell Paul Stovell Apples 27.30 Something bad happened Pears 17.85 Something bad happened
这是示例对象模型和用法:
static void Main(string[] args) { var customer = new Customer(); customer.FirstName = "Paul"; customer.LastName = "Stovell"; customer.Orders.Add(new Order(customer) { Price = 27.30M, SKU = "Apples"}); customer.Orders.Add(new Order(customer) { Price = 17.85M, SKU = "Pears"}); customer.Orders.Add(customer.Orders[0]); var output = new StringWriter(); var writer = new XmlTextWriter(output); writer.Formatting = Formatting.Indented; WriteComplexObject("Customer", customer, writer); Console.WriteLine(output.ToString()); Console.ReadKey(); } class Customer { private readonly List_orders = new List (); public Customer() { } public string FirstName { get; set; } public string LastName { get; set; } public string FullName { // Read-only property test get { return FirstName + " " + LastName; } } public List Orders { // Collections test get { return _orders; } } } class Order { private readonly Customer _customer; public Order(Customer customer) { _customer = customer; } public string SKU { get; set; } public decimal Price { get; set; } public string Currency { // A proprty that, for some reason, can't be read get { throw new Exception("Something bad happened"); } } public Customer Customer { get { return _customer; } } }
这是实施:
public static void WriteObject(string name, object target, XmlWriter writer) { WriteObject(name, target, writer, new List(), 0, 10, -1); } private static void WriteObject(string name, object target, XmlWriter writer, List recurringObjects, int depth, int maxDepth, int maxListLength) { var formatted = TryToFormatPropertyValueAsString(target); if (formatted != null) { WriteSimpleProperty(name, formatted, writer); } else if (target is IEnumerable) { WriteCollectionProperty(name, (IEnumerable)target, writer, depth, maxDepth, recurringObjects, maxListLength); } else { WriteComplexObject(name, target, writer, recurringObjects, depth, maxDepth, maxListLength); } } private static void WriteComplexObject(string name, object target, XmlWriter writer, List recurringObjects, int depth, int maxDepth, int maxListLength) { if (target == null || depth >= maxDepth) return; if (recurringObjects.Contains(target)) { writer.WriteStartElement(name); writer.WriteAttributeString("Ref", (recurringObjects.IndexOf(target) + 1).ToString()); writer.WriteEndElement(); return; } recurringObjects.Add(target); writer.WriteStartElement(name); writer.WriteAttributeString("Ref", (recurringObjects.IndexOf(target) + 1).ToString()); foreach (var property in target.GetType().GetProperties()) { var propertyValue = ReadPropertyValue(target, property); WriteObject(property.Name, propertyValue, writer, recurringObjects, depth + 1, maxDepth, maxListLength); } writer.WriteEndElement(); } private static object ReadPropertyValue(object target, PropertyInfo property) { try { return property.GetValue(target, null); } catch (Exception ex) { return ReadExceptionMessage(ex); } } private static string ReadExceptionMessage(Exception ex) { if (ex is TargetInvocationException && ex.InnerException != null) return ReadExceptionMessage(ex.InnerException); return ex.Message; } private static string TryToFormatPropertyValueAsString(object propertyValue) { var formattedPropertyValue = null as string; if (propertyValue == null) { formattedPropertyValue = string.Empty; } else if (propertyValue is string || propertyValue is IFormattable || propertyValue.GetType().IsPrimitive) { formattedPropertyValue = propertyValue.ToString(); } return formattedPropertyValue; } private static void WriteSimpleProperty(string name, string formattedPropertyValue, XmlWriter writer) { writer.WriteStartElement(name); writer.WriteValue(formattedPropertyValue); writer.WriteEndElement(); } private static void WriteCollectionProperty(string name, IEnumerable collection, XmlWriter writer, int depth, int maxDepth, List recurringObjects, int maxListLength) { writer.WriteStartElement(name); var enumerator = null as IEnumerator; try { enumerator = collection.GetEnumerator(); for (var i = 0; enumerator.MoveNext() && (i < maxListLength || maxListLength == -1); i++) { if (enumerator.Current == null) continue; WriteComplexObject(enumerator.Current.GetType().Name, enumerator.Current, writer, recurringObjects, depth + 1, maxDepth, maxListLength); } } catch (Exception ex) { writer.WriteElementString(ex.GetType().Name, ReadExceptionMessage(ex)); } finally { var disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } writer.WriteEndElement(); } }
我仍然有兴趣知道是否有更多经过试验和测试的解决方案.