1.x Document Storage

Topics: Customizing Orchard, General
Mar 27, 2014 at 4:57 PM
Edited Mar 27, 2014 at 5:01 PM
Started playing with the Document Storage capabilities in Orchard 1.x..

Some comments:

1) Part properties not part of the record set are stored as xml attributes to the part in the infoset. I find this could be dangerous and should be moved to child elements and CDataSections as to avoid any malformed XML possibilities.

2) Infosets seem to be missing a hierarchy that I would otherwise be built with classes and sub classes serialized into a document. The infoset should serialize and deserialize a class and it's subclasses either VIA xml serialization or json serialization.

3) Infoset support for classes are limited to what the programmer deemed as important. I tried to use a GUID for example and received an error. This is handled in the #3 if the .net serialization classes were implemented instead of what currently appears to be nothing more than a glorified name value pair.

4) Again related to above, I should be able to conform my serialization using the XMLAttributes in my class hierarchy.

I think the easiest way to solve the above is to check the TProperty attributes for an XMLElement Attribute. If found, an element is added to the xml, and M$ serialization is implemented to and fro that element.

This solves all my concerns above. I have bolded the changes I made.

I did a very basic test, here is the code I changed, I used JSON however..


-  Some file in Orchard.Framework.
namespace Orchard.ContentManagement {
  // An attribute identifies the part property should be JSON serialized.
  public class JSONSerialize : Attribute
    {

    }
}

XMLHelper.cs
    public static class XmlHelper {
       // JSON serialization added to XmlHelper
        public static string ToJSON<T>(this XElement el, T value)
        {
            var serializer = new JavaScriptSerializer();
            var serializedResults = serializer.Serialize(value);
            return serializedResults;
        }

        public static T FromJSON<T>(string value)
        {
            var serializer = new JavaScriptSerializer();
            var serializedResults = serializer.Deserialize<T>(value);
            return serializedResults;
        }
 .....
}

InfosetHelper.cs

 public static class InfosetHelper {

        public static TProperty Retrieve<TPart, TProperty>(this TPart contentPart,
            Expression<Func<TPart, TProperty>> targetExpression,
            TProperty defaultValue = default(TProperty),
            bool versioned = false) where TPart : ContentPart {

            var propertyInfo = ReflectionHelper<TPart>.GetPropertyInfo(targetExpression);
            var name = propertyInfo.Name;

            var infosetPart = contentPart.As<InfosetPart>();
            var el = infosetPart == null
                ? null
                : (versioned ? infosetPart.VersionInfoset.Element : infosetPart.Infoset.Element)
                .Element(contentPart.GetType().Name);
           // Check the property for the JSON attribute, this could be the XMLAttribute if we wish to use xml.
           if(propertyInfo.GetCustomAttributes(typeof(JSONSerialize), false).Length > 0)
            {
                var child = el.Element(name);
                if(child == null){
                    return Activator.CreateInstance<TProperty>();
                }
                return XmlHelper.FromJSON<TProperty>(child.Value);
            }
            else{
                var attr = el != null ? el.Attribute(name) : default(XAttribute);
                return attr == null ? defaultValue : XmlHelper.Parse<TProperty>(attr.Value);
                }
        }

        public static void Store<TPart, TProperty>(this TPart contentPart, 
            Expression<Func<TPart, TProperty>> targetExpression,
            TProperty value, bool versioned = false) where TPart : ContentPart {

            var partName = contentPart.GetType().Name;
            var infosetPart = contentPart.As<InfosetPart>();
            var propertyInfo = ReflectionHelper<TPart>.GetPropertyInfo(targetExpression);
            var name = propertyInfo.Name;
           // Check for the JSON attribute and serialize.
           if (propertyInfo.GetCustomAttributes(typeof(JSONSerialize), false).Length > 0)
            {
                StoreElement(infosetPart, partName, name, value, versioned);
            }
            else
            {
                Store(infosetPart, partName, name, value, versioned);
            }
        }

        public static void StoreElement<TProperty>(this InfosetPart infosetPart, string partName, string name, TProperty value, bool versioned = false)
        {
            var infoset = (versioned ? infosetPart.VersionInfoset : infosetPart.Infoset);
            var partElement = infoset.Element.Element(partName);
            var childElement = new XElement(name);

            if (partElement == null)
            {
                partElement = new XElement(partName);
                infoset.Element.Add(partElement);
            }
            if (partElement.Element(name) != null)
            {
                partElement.Element(name).Remove();
            }
            childElement.Add(new XCData(partElement.ToJSON(value)));
            partElement.Add(childElement);
        }