罗伯特·罗斯尼的帖子让我认为这可能比我想象的要简单。所以这是一个非常粗糙的尝试。它处理以下内容:
- 如果无法读取属性,则将异常打印为值
- 循环引用和多次出现。它使用ID与每个元素关联;如果一个元素出现两次,它只是指向ref ID。Ref ID对对象图是唯一的(我应该使用GUID,但这符合我的目的)。
- 它没有派生类型的问题
- 它不需要属性或特定构造函数或其他废话
- 它可以处理只读属性
这是输出的示例(在我的测试对象中,“订单”上的“货币”产品会抛出异常)。
<Customer Ref="1">
<FirstName>Paul</FirstName>
<LastName>Stovell</LastName>
<FullName>Paul Stovell</FullName>
<Orders>
<Order Ref="2">
<SKU>Apples</SKU>
<Price>27.30</Price>
<Currency>Something bad happened</Currency>
<Customer Ref="1" />
</Order>
<Order Ref="3">
<SKU>Pears</SKU>
<Price>17.85</Price>
<Currency>Something bad happened</Currency>
<Customer Ref="1" />
</Order>
<Order Ref="2" />
</Orders>
</Customer>
以下是示例对象模型和用法:
示例:
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<Order> _orders = new List<Order>();
public Customer()
{
}
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return FirstName + " " + LastName; }
}
public List<Order> Orders
{
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
{
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<object>(), 0, 10, -1);
}
private static void WriteObject(string name, object target, XmlWriter writer, List<object> 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<object> 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<object> 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();
}
}
我仍然很想知道是否有更多经过验证的解决方案。