Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Robert Rossney's post made me think it's probably less work than I thought. So here's a very rough attempt. It handles the following:</p> <ul> <li>If it is unable to read a property, it prints the exception as the value</li> <li>Cyclic references and multiple occurrences. It associates an ID with each element; if an element appears twice, it just points the ref ID. The Ref ID is unique to the object graph (I should probably use a GUID, but this suits my purposes).</li> <li>It has no problems with derived types</li> <li>It requires no attributes or specific constructors or other nonsense</li> <li>It can handle read-only properties</li> </ul> <p>Here's an example of the output (in my test objects, the "Currency" product on the Order throws an exception). </p> <pre><code>&lt;Customer Ref="1"&gt; &lt;FirstName&gt;Paul&lt;/FirstName&gt; &lt;LastName&gt;Stovell&lt;/LastName&gt; &lt;FullName&gt;Paul Stovell&lt;/FullName&gt; &lt;Orders&gt; &lt;Order Ref="2"&gt; &lt;SKU&gt;Apples&lt;/SKU&gt; &lt;Price&gt;27.30&lt;/Price&gt; &lt;Currency&gt;Something bad happened&lt;/Currency&gt; &lt;Customer Ref="1" /&gt; &lt;/Order&gt; &lt;Order Ref="3"&gt; &lt;SKU&gt;Pears&lt;/SKU&gt; &lt;Price&gt;17.85&lt;/Price&gt; &lt;Currency&gt;Something bad happened&lt;/Currency&gt; &lt;Customer Ref="1" /&gt; &lt;/Order&gt; &lt;Order Ref="2" /&gt; &lt;/Orders&gt; &lt;/Customer&gt; </code></pre> <p>Here's the sample object model and usage:</p> <pre><code>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&lt;Order&gt; _orders = new List&lt;Order&gt;(); 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&lt;Order&gt; 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; } } } </code></pre> <p>Here's the implementation:</p> <pre><code>public static void WriteObject(string name, object target, XmlWriter writer) { WriteObject(name, target, writer, new List&lt;object&gt;(), 0, 10, -1); } private static void WriteObject(string name, object target, XmlWriter writer, List&lt;object&gt; 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&lt;object&gt; recurringObjects, int depth, int maxDepth, int maxListLength) { if (target == null || depth &gt;= 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 &amp;&amp; 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&lt;object&gt; recurringObjects, int maxListLength) { writer.WriteStartElement(name); var enumerator = null as IEnumerator; try { enumerator = collection.GetEnumerator(); for (var i = 0; enumerator.MoveNext() &amp;&amp; (i &lt; 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(); } } </code></pre> <p>I would still be interested to know if there are more tried and tested solutions.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload