/// <summary> /// Turn the innerText of an XML node into a DateTime and return that DateTime /// </summary> /// <param name="_object">The object to deserialize into</param> /// <param name="_node">The node containing the DateTime string</param> /// <param name="_deserializer">The serializer- not used</param> /// <returns>a DateTime object</returns> public bool Deserialize(CWorkingObject _object, XmlElement _node, CDeserializer _deserializer) { var sb = new StringBuilder(_node.InnerText); _object.Set(sb); return(true); }
/// <summary> /// Create an Array from the data in the Xml and return that array. /// </summary> /// <param name="_xml">The XML containing the data for the array</param> /// <param name="_type">The Type of the array, as inferred from the framework</param> /// <param name="_workingObject"> /// A working object (optional) that must be an Array (if its not null) /// </param> /// <returns></returns> private void DeserializeArray(XmlElement _xml, Type _type, CWorkingObject _workingObject) { var condensedArray = FindCondensedArray(_xml); var inferredLength = (condensedArray != null) ? condensedArray.Length : -1; var arrayHelper = CreateArrayDeserializationHelper(_xml, _type, _workingObject, inferredLength); // At this stage, we know its a Rank 1 array. Figure out the serialization of that // array Single child node that's a text node is an assumed indicator that the // "Condensed" format was used to serialize if (condensedArray != null) { DeserializeArrayFromCondensedArray(condensedArray, arrayHelper); } else { // There are individual child XmlElements for each array element. "Index" // attributes can be used to "skip" null array values. DeserializeArrayFromElements(_xml, arrayHelper); } if (!_workingObject.IsSet) { _workingObject.Set(arrayHelper.Array); } }
/// <summary> /// The Type uses EntitySemantics. See <see cref="CEntityTypeData"/> for more info on /// EntitySemantics /// </summary> /// <param name="_xml">The XML containing the data</param> /// <param name="_type"> /// The Type that has already been determined to use Entity Semantics /// </param> /// <param name="_workingObject">Where to put the resulting object</param> private void DeserializeUsingEntitySemantics(XmlElement _xml, Type _type, CWorkingObject _workingObject) { var obj = _workingObject.GetExistingOrCreateNew(_type); var typeData = CEntityTypeData.GetTypeData(_type); for (var i = 0; i < typeData.NonCollectionProperties.Length; i++) { var prop = typeData.NonCollectionProperties[i]; var xml = _xml[prop.Name]; if (xml != null) // There was some XmlElement for the property { // This isn't a Field, and we're using EntitySemantics, so use there's // nothing to query PushCurrentField(null); var propVal = FrameworkDeserialize(xml, prop.PropertyType); prop.SetValue(obj, propVal); PopField(); // pop the null we pushed } } for (var i = 0; i < typeData.CollectionProperties.Length; i++) { var colProp = typeData.CollectionProperties[i]; } }
/// <summary> /// The framework will deserialize the object. No more surrogates or other "exceptional" /// behaviour. /// </summary> /// <param name="_xml"></param> /// <param name="_type"></param> /// <param name="_workingObject"></param> private void HandleDeserialization(XmlElement _xml, Type _type, CWorkingObject _workingObject) { // Strings are really easy- just return the "InnerText" if (_type == TYPEOF_STRING) { _workingObject.Set(Environment.ExpandEnvironmentVariables(_xml.InnerText)); } // Primitives are also pretty easy, only because of the "Convert" class else if (_type.IsPrimitive) { _workingObject.Set(Convert.ChangeType(_xml.InnerText, _type)); } // Check for an Array else if (_type.IsArray || XmlExtensions.HasAttribute(_xml, m_context.ArrayAttributeName)) { DeserializeArray(_xml, _type, _workingObject); } else if (_type.IsEnum) { _workingObject.Set(Enum.Parse(_type, _xml.InnerText, false)); } else // Handle ref-type fields and base classes. { DeserializeReferenceType(_xml, _type, _workingObject); } }
/// <summary> /// Turn the innerText of an XML node into a DateTime and return that DateTime /// </summary> /// <param name="_object">The object to deserialize into</param> /// <param name="_node">The node containing the DateTime string</param> /// <param name="_deserializer">The serializer- not used</param> /// <returns>a DateTime object</returns> public bool Deserialize(CWorkingObject _object, XmlElement _node, CDeserializer _deserializer) { var dateData = long.Parse(_node.InnerText, NumberStyles.AllowHexSpecifier); _object.Set(DateTime.FromBinary(dateData)); return(true); }
/// <summary> /// Check the easiest forms of deserialization- Null and RefTo an object already /// deserialized. /// </summary> /// <param name="_xml">The XML that's being deserialized</param> /// <param name="_workObj"> /// The Object that was deserialized (when the return value is TRUE) /// </param> /// <returns> /// TRUE means that the _workObj parameter contains valid data and nothing further /// should be done, FALSE means that no deserialization was performed. /// </returns> private bool CheckNullAndReference(XmlElement _xml, CWorkingObject _workObj) { // Check the XML to see if this is a NULL object reference. If it is, then return // TRUE. if (XmlExtensions.HasAttribute(_xml, m_context.NullAttributeName)) { return(true); } // Check the XML to see if its referring to some other object. var referTo = XmlExtensions.GetAttributeValue(_xml, m_context.ReferToAttributeName); if (referTo != null) // There was a reference to another object, so handle it and return that other object. { // Check the table for the RefID to get the object. The reference must be in the // table already because forward-looking references are not supported. if (!m_references.TryGetValue(referTo, out var obj)) { throw new XUnknownReference("All object-references must be to backward-defined objects. The RefID " + referTo + " has not been defined yet."); } _workObj.Set(obj); return(true); } // Check to see if this element is associating the XML with a reference ID var refId = XmlExtensions.GetAttributeValue(_xml, m_context.ReferenceIdAttributeName); if (refId != null) { _workObj.SetRefInfo(this, refId); } return(false); }
/// <summary> /// This is the Deserializer to turn a UTC "Complete date plus hours, minutes and seconds" string /// back into a DateTime structure. /// </summary> /// <param name="_workingObject">The object that is to receive the new DateTime structure</param> /// <param name="_parentNode">The node whose InnerText contains the UTC string</param> /// <param name="_deserializer">The deserializer- not used.</param> /// <returns>"true", because this routine completely deserializes the DateTime</returns> public bool Deserialize(CWorkingObject _workingObject, XmlElement _parentNode, CDeserializer _deserializer) { var asString = _parentNode.InnerText; var dt = FromString(asString); _workingObject.Set(dt); return(true); }
/// <summary> /// Given an XmlElement and a "CWorkingObject" instance, try to deserialize the Xml into /// the working object. The surrogate may be responsible for object creation- the /// _workingObject should be checked to see if an object has already been created (by, /// for instance, a super-class). All of the information for the object should be /// wholely contained within the XmlElement supplied. The implementation should never /// need to "hunt around" the XML DOM for information about the object. /// </summary> /// <param name="_workingObject"> /// The container for object that is being deserialized. If the "WorkingObject" property /// is a NULL value, then the deserializer is expected to create the object and use the /// <see cref="CWorkingObject.Set"/> method to assign the newly created object to the /// working object. If it is NOT a null value, then the deserializer is not allowed to /// modify this "Working Object" value. /// </param> /// <param name="_parentElement"> /// The XmlElement containing the data to populate the object with /// </param> /// <param name="_deserializer"> /// The CDeserializer instance making this call- This can be used to get the context /// information or any other relevent information. /// </param> /// <returns> /// TRUE if the surrogate was able to completely deserialize the object, FALSE if the /// framework should perform its "default" function. /// </returns> public bool Deserialize(CWorkingObject _workingObject, XmlElement _parentElement, CDeserializer _deserializer) { if (Surrogate1.Deserialize(_workingObject, _parentElement, _deserializer)) { return(true); } return(Surrogate2.Deserialize(_workingObject, _parentElement, _deserializer)); }
/// <summary> /// Given information about the current state, create an ArrayDeserializationHelper to /// help with the deserialization process /// </summary> /// <param name="_xml">The XML containing the Array information</param> /// <param name="_type">The Type of the array expected</param> /// <param name="_workingObject"> /// A "Working Object", likely from a surrogate that didn't complete the deserialization /// </param> /// <param name="_inferredLength"> /// The Inferred Length of the array, from a "Condensed Array" already found in the XML /// </param> /// <returns> /// A properly formed <see cref="CArrayDeserializationHelper"/> object. /// </returns> private CArrayDeserializationHelper CreateArrayDeserializationHelper(XmlElement _xml, Type _type, CWorkingObject _workingObject, int _inferredLength) { CArrayDeserializationHelper arrayHelper; if (_workingObject.IsSet) { arrayHelper = new CArrayDeserializationHelper(_workingObject.WorkingObject as Array); } else { // Make sure that the array type actually is an array and it has an ElementType var arrType = _type; if (!arrType.IsArray) { throw new XDeserializationError( "The Type specified is not an array or it does not have an ElementTypes associated with it- " + arrType.ToString()); } // Get info from the XML var sLengths = XmlExtensions.GetAttributeValue(_xml, m_context.ArrayAttributeName); var lengths = sLengths?.Split(',').Select(s => int.Parse(s)).ToArray(); var sLowerBounds = XmlExtensions.GetAttributeValue(_xml, m_context.ArrayLowerBoundAttribute); var lowerBounds = sLowerBounds?.Split(',').Select(s => int.Parse(s)).ToArray(); if (lengths == null) { if (_inferredLength > -1) { lengths = new int[] { _inferredLength } } ; else { lengths = new int[] { InferArrayLength(_xml) } }; // This assumes a 1-dim array at this point. } arrayHelper = new CArrayDeserializationHelper(arrType.GetElementType(), lengths, lowerBounds); } return(arrayHelper); }
/// <summary> /// Check to see if there are surrogates that handle the deserialization. /// </summary> /// <param name="_xml">The XML containing the object data</param> /// <param name="_type">The TYPE that we're trying to deserialize</param> /// <param name="_workingObject"> /// A "Working Object" that deserializers use to determine if they need to create a new /// object or if they have an object that they can deserialize into. /// </param> /// <returns> /// TRUE if a surrogate was successful in completing the deserialization of the object, /// or FALSE if the surrogates were not able to complete all deserialization /// </returns> private bool ApplySurrogates(XmlElement _xml, Type _type, CWorkingObject _workingObject) { var isComplete = false; _workingObject.WorkingType = _type; // Check for EntitySemantics as a special and overriding Surrogate method The // UseEntitySemantics on the XML element is ignored, as the destination Type gets to // define how it gets deserialized (the attribute may be little more than a Hint to // what happened at serializatiion time) See for more information on EntitySemantics if (CEntityTypeData.UsesEntitySemantics(_type)) { DeserializeUsingEntitySemantics(_xml, _type, _workingObject); return(true); } // Check for external surrogates var externalSurrogate = GetExternalSurrogate(_type); if (externalSurrogate != null) { isComplete = externalSurrogate.Deserialize(_workingObject, _xml, this); } if (isComplete) { return(true); } // Check the Implicit surrogates if the External surrogate(s) didn't finish the // deserialization var typeData = CTypeData.GetTypeData(_type); var implicitSurrogate = typeData.ImplicitSurrogate; if (implicitSurrogate != null && implicitSurrogate.HasSurrogate) { isComplete = implicitSurrogate.Deserialize(_workingObject, _xml, this); } if (isComplete) { return(true); } return(false); }
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // The rest of the file contains the code to actually serialize or deserialize using the data constructed above. // /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// <summary> /// Create the parameter array for an implicit surrogate using the data created by the rest of this class. /// </summary> /// <param name="_implicit">The implicit surrogate</param> /// <param name="_xmlToSerializeTo">The XML that the object will serialize itself to</param> /// <param name="_framework">The Context that the object will use (optionally) to serialize the data.</param> /// <param name="_object">The "Working Object" useful to a deserializer</param> /// <returns>an object[] containing the parameter data as dictated by the implicitSurrogate object</returns> private static object[] BuildParamArray(CImplicitSurrogate _implicit, XmlElement _xmlToSerializeTo, CFramework _framework, CWorkingObject _object) { var arr = new object[_implicit.m_parameterCount]; arr[_implicit.m_indexXml] = _xmlToSerializeTo; if (_implicit.m_indexFramework != -1) { arr[_implicit.m_indexFramework] = _framework; } if (_implicit.m_indexObject != -1) { arr[_implicit.m_indexObject] = _object; } return(arr); }
/// <summary> /// This is the recursively-called routine that will actually direct the deserialization /// using the analysis of the xml node being deserialized. /// </summary> /// <param name="_xml">The XML containing the data for the object</param> /// <param name="_defaultType"> /// The Type that is expected for this object- this may be overridden by the XML /// </param> /// <returns>The object that was created from the XML</returns> public object FrameworkDeserialize(XmlElement _xml, Type _defaultType) { if (_xml == null) { return(null); } var workingObject = new CWorkingObject(); // Figure out if this process can be cut way short with "null" or "reference" nodes if (CheckNullAndReference(_xml, workingObject)) { return(workingObject.WorkingObject); } // Figure out the Type that we should be working with var oType = GetTypeFromXmlOrDefault(_xml, _defaultType); if (oType == null) { throw new XDeserializationError( "For the XmlElement named '" + _xml.Name + "', there was no Type information available (Type Attribute: '" + m_context.TypeAttributeName + "')"); } // Check for Surrogates first, and if not then handle deserialization with this // class. if (!ApplySurrogates(_xml, oType, workingObject)) { // An invalid type cannot be deserialized if (IsValidType(oType)) { HandleDeserialization(_xml, oType, workingObject); // Let the Framework do its thing } } return(workingObject.WorkingObject); }
/// <summary> /// The Type is a reference type with zero or more fields, and it is to be deserialized /// using the Framework's algorithm /// </summary> /// <param name="_xml">The XML containing the field data</param> /// <param name="_type">The Type that is to be deserialized</param> /// <param name="_workingObject"> /// The working object to be used if it is set (create new object instance if not) /// </param> /// <returns>The newly deserialized object</returns> private void DeserializeReferenceType(XmlElement _xml, Type _type, CWorkingObject _workingObject) { var typeData = CTypeData.GetTypeData(_type); if (!_workingObject.IsSet) { // Check to see if we can do anything with an explicit constructor (likely to be // private) var o = typeData.TryExplicitConstructor(); if (o == null) { o = Activator.CreateInstance(_type); } if (o == null) { throw new XDeserializationError("Could not create a new object of type " + _type.ToString()); } _workingObject.Set(o); } var currentObject = _workingObject.WorkingObject; while (typeData != null) { DeserializeObjectFields(_xml, currentObject, typeData); ClearIgnoredFields(); typeData = typeData.BaseType; if (typeData != null) { if (ApplySurrogates(_xml, typeData.Type, _workingObject)) { typeData = null; } } } }
/// <summary> /// Deserialize an object of this type. /// </summary> /// <param name="_object">A reference to an object that is being deserialized. NULL --> The serializer needs to create the object!</param> /// <param name="_xml">The XML node that contains the data for the deserialization</param> /// <param name="_framework">The serialization context to help with deserialization</param> /// <returns>TRUE if the deserialization is complete, FALSE if the framework needs to complete the deserialization</returns> internal bool Deserialize(CWorkingObject _object, XmlElement _xml, CFramework _framework) { var surrogate = m_implicitDeserializer; if (surrogate == null) { return(false); } var paramArray = BuildParamArray(surrogate, _xml, _framework, _object); var isComplete = surrogate.m_method.Invoke(null, paramArray); // deserializer is always a static method if (isComplete is bool) { return((bool)isComplete); } else { return(true); } // An implicit deserializer is assumed to completely serialize the Type and all base classes unless explicitly stated otherwise. }
/// <summary> /// Turn the innerText of an XML node into a guid and return that guid /// </summary> /// <param name="_node">The node containing the GUID string</param> /// <param name="_object">The object to deserialize into</param> /// <param name="_deserializer">The Deserializer- not used</param> /// <returns>a GUID object</returns> public bool Deserialize(CWorkingObject _object, XmlElement _node, CDeserializer _deserializer) { _object.Set(new Guid(_node.InnerText)); return(true); }