Reflective XML Serializer with Recursion Detection

by Mwwhited 7. June 2009 11:06

Not being able to serialize Anonymous Types with the build in XmlSerializer was kind of a bummer for me, so I created this set of extension methods. Another feature I added at recursion detection based on the .GetHasCode() to prevent recursive object graphs from causing stack overflows. It's ugly... but it works... I was going for a proof of concept so I'm happy for now. -Enjoy, Matt


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Xml;
using System.Runtime.Serialization;
using System.IO;
using System.Xml.Serialization;
using System.Collections.ObjectModel;
using System.Diagnostics;

namespace WhitedUS.Libs.Converters
{
    public static class ObjectConverters
    {
        public static void WriteXml(this object input, Stream stream)
        {
            input.WriteXml(stream, string.Empty);
        }
        public static void WriteXml(this object input, Stream stream, string defaultNode)
        {
            if (input == null)
                return;

            if (!stream.CanWrite)
                throw new InvalidOperationException("Can not write to stream");

            if (input is XmlNode || input is XNode)
            {
                var buffer = Encoding.UTF8.GetBytes(input.ToXml());
                stream.Write(buffer, 0, buffer.Length);
            }
            else
            {
                if (input.HasDataContract())
                    input.SerializeDataContact(stream);
                else if (input.IsExtendedPrimitive() || input is IXmlSerializable)
                    input.SerializeXml(stream);
                else
                {
                    var ret = input.ToXml(defaultNode, null);
                    if (ret != null)
                    {
                        var buffer = ret.ToString().ToByteArray();
                        stream.Write(buffer, 0, buffer.Length);
                    }
                }
            }
        }

        public static string ToXml(this object input)
        {
            if (input == null)
                return null;

            if (input is XmlNode)
                return ((XmlNode)input).OuterXml;
            else if (input is XNode)
                return input.ToString();
            else
            {
                if (input.HasDataContract())
                {
                    using (var ms = new MemoryStream())
                    {
                        input.SerializeDataContact(ms);
                        return ms.Rewind().ToEncodedString();
                    }
                }
                else if (input.IsExtendedPrimitive() || input is IXmlSerializable)
                {
                    using (var ms = new MemoryStream())
                    {
                        input.SerializeXml(ms);
                        return ms.Rewind().ToEncodedString();
                    }
                }
                else
                {
                    var ret = input.ToXml(null, null);
                    if (ret == null)
                        return null;
                    else
                        return ret.ToString();
                }
            }
        }

        public static bool HasDataContract(this object input)
        {
            if (input != null)
                return input.GetType().GetCustomAttributes(typeof(DataContractAttribute), true).Length > 0;
            return false;
        }

        public static void SerializeDataContact(this object input, Stream stream)
        {
            if (input == null)
                return;

            if (!stream.CanWrite)
                throw new InvalidOperationException("Can not write to stream");

            new DataContractSerializer(input.GetType()).WriteObject(stream, input);
        }

        public static void SerializeXml(this object input, Stream stream)
        {
            if (input == null)
                return;

            if (!stream.CanWrite)
                throw new InvalidOperationException("Can not write to stream");

            new XmlSerializer(input.GetType()).Serialize(stream, input);
        }

        public static XNode ToXNode(this object input)
        {
            if (input == null)
                return null;

            if (input is XNode)
                return (XNode)input;

            var ret = input.ToXml();
            if (string.IsNullOrEmpty(ret))
                return null;
            return XElement.Parse(ret);
        }

        public static bool IsExtendedPrimitive(this object input)
        {
            if (input == null)
                return false;

            var inputType = input.GetType();
            return
                inputType.IsPrimitive ||
                input is string ||
                input is DateTime
                ;
        }

        public static bool CanSerializeXml(this object input)
        {
            if (input == null)
                return false;

            return input is XNode ||
                input is XmlNode ||
                input is IXmlSerializable ||
                input.IsExtendedPrimitive() ||
                input.HasDataContract()
                ;
        }

        public static XNode ToXml(this object input, string nodeName)
        {
            return input.ToXml(nodeName, null);
        }

        public static XNode ToXml(this object input, string nodeName, Collectioncontext) { if (input == null) return null; var inputType = input.GetType(); string xmlName = null; if (string.IsNullOrEmpty(nodeName)) xmlName = inputType.Name; else xmlName = nodeName; if (input.CanSerializeXml()) return new XElement(XmlConvert.EncodeName(xmlName), input.ToXNode()); if (context == null) { return ToXml(input, nodeName, new Collection()); } else { var props = inputType.GetProperties().Where(pi => pi.CanRead && pi.GetGetMethod().GetParameters().Length == 0); var hashCode = input.GetHashCode(); if (context.Contains(input)) return new XElement(XmlConvert.EncodeName(xmlName), new XElement("HashCode_Pointer", hashCode)); else { context.Add(input); return new XElement(XmlConvert.EncodeName(xmlName), new XAttribute("HashCode_ID", hashCode), from pi in props let v = pi.GetValue(input, null) where v != null select v.ToXml(pi.Name, context) ); } throw new NotImplementedException(); } } } } 

Tags: , , , ,

Programming

Comments are closed

Powered by BlogEngine.NET 1.5.0.7
Theme by Mads Kristensen

RecentPosts

Badges