private void WriteXml(XmlWriter writer, object o, TypeDefinition type, string name = null, bool writeReservedObject = false, bool forceSaveType = false) { if (type.IsPrimitive) { WriteXmlPrimitive(o, writer, type, name); return; } if (type.IsDictionary) { WriteXmlDictionary(o, writer, type, name, forceSaveType); return; } if (type.IsArray || type.IsEnumerable) { WriteXmlArray(o, writer, type, name, forceSaveType); return; } writer.WriteStartElement(name ?? type.Name); // Get configuration for type var configuration = GetConfiguration(type.Type); if (configuration != null) { if (configuration.IsObjectReference) { var objectId = configuration.GetObjectId(o); if (ExternalReferencesObjects.ContainsKey(type.FullName.Replace('.', '_') + Underscore + objectId)) { if (forceSaveType) { writer.WriteAttributeString(Type, type.FullName); } writer.WriteAttributeString(Ref, objectId); writer.WriteEndElement(); return; } var key = type.FullName + Underscore + objectId; if (writeReservedObject && _reservedReferencesObjects.ContainsKey(key)) { _reservedReferencesObjects.Remove(key); } else if (_referencesObjects.ContainsKey(key) || _reservedReferencesObjects.ContainsKey(key)) { if (forceSaveType) { writer.WriteAttributeString(Type, type.FullName); } writer.WriteAttributeString(Ref, objectId); writer.WriteEndElement(); return; } writer.WriteAttributeString(Type, type.FullName); writer.WriteAttributeString(Id, objectId); _referencesObjects.Add(key, o); } else { writer.WriteAttributeString(Type, type.FullName); } if (configuration.Version > 0) { writer.WriteAttributeString(Version, configuration.Version.ToString(CultureInfo.InvariantCulture)); } if (configuration.IsCustomSerializer) { configuration.WriteObject(writer, o); writer.WriteEndElement(); return; } } else { writer.WriteAttributeString(Type, type.FullName); } var properties = type.Properties; foreach (var propertyInfo in properties) { var propertyValue = propertyInfo.GetValue(o); if (propertyValue == null) { continue; } var defType = TypeDefinitionCache.GetDefinition(propertyValue.GetType()); if (defType.IsObjectToSerialize || defType.IsArray || defType.IsEnumerable) { WriteXml(writer, propertyValue, defType, propertyInfo.Name, forceSaveType: propertyInfo.IsWritable && propertyInfo.TypeDefinition.FullName != defType.FullName); } else if (defType.IsEnum) { writer.WriteStartElement(propertyInfo.Name); writer.WriteString(propertyValue.ToString()); writer.WriteEndElement(); } else { bool toEncrypt = false; if (configuration != null) { if (configuration.CheckPropertyEncryption(propertyInfo.Name)) { toEncrypt = true; } } if (propertyInfo.TypeDefinition.Type.FullName != defType.Type.FullName) { WriteXmlPrimitive(propertyValue, writer, defType, propertyInfo.Name, toEncrypt, defType.Type.FullName); } else { WriteXmlPrimitive(propertyValue, writer, defType, propertyInfo.Name, toEncrypt); } } } writer.WriteEndElement(); }
private object ReadXml(XElement currentNode, TypeDefinition type, object instance = null) { if (type.IsPrimitive) { return(PrimitiveValueTools.GetPrimitiveValue(currentNode.Value, type, currentNode.Name.LocalName)); } if (type.IsDictionary) { return(ReadXmlDictionary(currentNode, type)); } if (type.IsArray || type.IsEnumerable) { return(ReadXmlArray(currentNode, type, instance)); } if (currentNode == null) { return(null); } // Retrieve type from XML (Property can be base type. In xml can be saved inherited object) or type of get property var currentNodeDef = GetElementTypeDefinition(currentNode) ?? type; // Get configuration for type var configuration = GetConfiguration(currentNodeDef.Type); //Console.Write($"{currentNodeDef.FullName} {currentNodeDef.Type} {configuration?.IsObjectReference}"); //Console.WriteLine(); if (configuration != null) { // Run migrator if exists if (configuration.Version > 0) { configuration.Map(currentNodeDef.Type, currentNode); } // run custom serializer if exists if (configuration.IsCustomSerializer) { return(configuration.ReadObject(currentNode)); } } //Console.Write($"{currentNodeDef.FullName} {currentNodeDef.Type} {configuration?.IsObjectReference}"); //Console.WriteLine(); // Create new instance if not exists var currentObject = instance ?? currentNodeDef.ObjectActivator(); //Console.Write($"1"); //Console.WriteLine(); if (configuration != null) { //Console.Write($"2"); //Console.WriteLine(); if (configuration.IsObjectReference) { string refId = currentNode.Attribute(Ref)?.Value; //Console.Write($"{refId}"); //Console.WriteLine(); if (!string.IsNullOrEmpty(refId)) { //Console.Write($"3"); //Console.WriteLine(); if (ExternalReferencesObjects.ContainsKey(currentNodeDef.FullName.Replace('.', '_') + Underscore + refId)) { //Console.Write($"4"); //Console.WriteLine(); //Console.Write($"{currentNodeDef.FullName.Replace('.', '_') + Underscore + refId}"); //Console.WriteLine(); //Console.Write($"{ExternalReferencesObjects[currentNodeDef.FullName.Replace('.', '_') + Underscore + refId]}"); //Console.WriteLine(); //if (currentNodeDef.FullName.Replace('.', '_') + Underscore + refId == "InSimu_NewEditor_Data_Symptoms_SymptomFamily_Hasmenes") // throw new Exception(); return(ExternalReferencesObjects[currentNodeDef.FullName.Replace('.', '_') + Underscore + refId]); } //Console.Write(currentNodeDef.FullName.Replace('.', '_') + Underscore + refId); //Console.WriteLine(); var key = currentNodeDef.FullName + Underscore + refId; if (_referencesObjects.ContainsKey(key)) { return(_referencesObjects[key]); } _referencesObjects.Add(key, currentObject); } string objectId = currentNode.Attribute(Id)?.Value; if (!string.IsNullOrEmpty(objectId)) { var key = currentNodeDef.FullName + Underscore + objectId; if (_referencesObjects.ContainsKey(key)) { currentObject = _referencesObjects[key]; } else { _referencesObjects.Add(key, currentObject); } } } } // Read all elements foreach (var xElement in currentNode.Elements()) { var localName = xElement.Name.LocalName; var value = xElement.Value; var propertyInfo = type.GetProperty(localName); if (propertyInfo == null) { throw new InvalidOperationException("Missing property " + currentNode.Name.LocalName + "\\" + localName); } var propertyDef = propertyInfo.TypeDefinition; if (xElement.HasAttributes && xElement.Attribute(Type) != null) { // If type of property is saved in xml, we need check type of object actual assigned to property. There may be a base type. Type targetType = TypeDefinitionCache.GetType(xElement.Attribute(Type).Value); var targetTypeDef = TypeDefinitionCache.GetDefinition(targetType); var obj = propertyInfo.GetValue(currentObject); if ((obj == null || obj.GetType() != targetType) && targetTypeDef.ObjectActivator != null) { obj = targetTypeDef.ObjectActivator(); } var obj2 = ReadXml(xElement, targetTypeDef, obj); propertyInfo.SetValue(currentObject, obj2); } else if (propertyDef.IsObjectToSerialize || propertyDef.IsArray || propertyDef.IsEnumerable || propertyDef.IsDictionary) { //If xml does not contain type but we known that it is object var obj = propertyInfo.GetValue(currentObject); var obj2 = ReadXml(xElement, propertyDef, obj); //Console.Write($"{obj} {obj2}"); //Console.WriteLine(); bool isObjValid = (obj != null); try { //HACK: This is how unitialized objects created as default property values are invalidated. It has caused major headaches. Touch with care and find a proper solution if possible! isObjValid = isObjValid && ((obj is ICollection) || !$"{obj}".StartsWith("<new")); isObjValid = isObjValid && ((obj is ICollection) || !$"{obj}".StartsWith("<empty")); } catch { isObjValid = false; } if ((!isObjValid || propertyDef.IsDictionary) && obj2 != null) { //Console.Write($"***"); //Console.WriteLine(); propertyInfo.SetValue(currentObject, obj2); } } else { if (string.IsNullOrEmpty(value)) { continue; } if (configuration != null) { if (configuration.CheckPropertyEncryption(propertyInfo.Name)) { var algorithm = GetEncryptionAlgorithm(); if (algorithm != null) { value = algorithm.Decrypt(value); } } } object primitive = PrimitiveValueTools.GetPrimitiveValue(value, propertyDef, xElement.Name.LocalName); propertyInfo.SetValue(currentObject, primitive); } } return(currentObject); }