// ---------------------------------------------------------------------------------------- #endregion #region StructureValueType constructors // ---------------------------------------------------------------------------------------- // StructureValueType constructors // ---------------------------------------------------------------------------------------- /// <summary> /// Initializes a new instance of the <see cref="StructureValueType"/> class. /// </summary> /// <param name="key">The key.</param> /// <param name="type">The type.</param> public StructureValueType(string key, Type type, bool isArrayItem) : base(key, isArrayItem) { this.type = type; this.useCultureSensitiveConvert = typeof(double).Equals(type) || typeof(decimal).Equals(type) || typeof(Single).Equals(type); this.underlyingNullableType = Nullable.GetUnderlyingType(type); this.isNullableType = underlyingNullableType != null; if (isNullableType) { this.nullableUnderlyingTypeConverter = Structure.DetermineStructure(underlyingNullableType, key, null, isArrayItem); } }
/// <summary> /// Deserializes the specified json string. /// </summary> /// <param name="json">The json.</param> /// <param name="currentReadIndex">Index of the current read.</param> /// <param name="context">The context.</param> /// <returns></returns> public override object Deserialize(string json, ref int currentReadIndex, SerializationContext context) { // check if value is null int expectedSepIndex = currentReadIndex + keyLength; if (json[expectedSepIndex] == 'n' && json[expectedSepIndex + 1] == 'u' && json[expectedSepIndex + 2] == 'l' && json[expectedSepIndex + 3] == 'l') { currentReadIndex = expectedSepIndex + 4; return(null); } if (json[expectedSepIndex + 1] == Structure.CharQuotationMark && json[expectedSepIndex + 2] == '#' && json[expectedSepIndex + 3] == 't' && json[expectedSepIndex + 4] == 'y' && json[expectedSepIndex + 5] == 'p' && json[expectedSepIndex + 6] == 'e' ) { return(DeserializeSpecialType(json, ref currentReadIndex, context, expectedSepIndex)); } if (isObject) { Type objectTargetType = null; if (unknownTypeResolver != null) { context.Key = this.key; context.ValueStartIndex = currentReadIndex; context.ArrayIndex = null; context.JsonString = json; if (type.IsInterface) { context.InterfaceType = this.type; } objectTargetType = unknownTypeResolver(context); if (context.InterfaceType != null) { context.InterfaceType = null; } } if (objectTargetType == null) { objectTargetType = Structure.GetDefaultType(json, expectedSepIndex); if (objectTargetType == null) { throw new InvalidOperationException("Can't determine target type for JSON key: \"" + context.Key + "\""); } } IJsonTypeStructure currentObjectStructure; if (!typeSerializerCache.TryGetValue(objectTargetType, out currentObjectStructure)) { currentObjectStructure = Structure.DetermineStructure(objectTargetType, this.key, context, this.isArrayItem); typeSerializerCache[objectTargetType] = currentObjectStructure; } return(currentObjectStructure.Deserialize(json, ref currentReadIndex, context)); } else { if (json[currentReadIndex] != Structure.CharLeftBrace && !isArrayItem) { if (json[currentReadIndex] == Structure.CharComma) { currentReadIndex += 1; } expectedSepIndex = currentReadIndex + keyLength - 1; if (json[expectedSepIndex] == Structure.CharColon) { currentReadIndex = expectedSepIndex + 1; } else { throw new Exception("Unexpected JSON data!"); } } if (this.concreteTargetType == null) { // try get static target type if (type.IsInterface || type.IsAbstract) { context.InterfaceType = type; context.Key = key; Type itemTypeResolved = unknownTypeResolver(context); if (itemTypeResolved != null) { concreteTargetType = itemTypeResolved; } context.InterfaceType = null; } else { concreteTargetType = type; } } // create target object object target; try { target = TypeService.CreateInstance(this.concreteTargetType); } catch (Exception ex) { throw new Exception("Can't create object instance for \"" + this.concreteTargetType + "\" - \"" + type + "\"!", ex); } object oldParentObj = context.ParentObject; context.ParentObject = target; // jump over opening brace currentReadIndex += 1; for (int propIndex = 0; propIndex < objectStructure.Length; propIndex++) { IJsonTypeStructure structure = objectStructure[propIndex]; var setPropertyAccessor = setAccessorByPropertyIndex[propIndex]; // check out of order json int expectedKeyStartIndex = currentReadIndex + 1; int actualKeyIndex = json.IndexOf(structure.Key, expectedKeyStartIndex); if (actualKeyIndex != expectedKeyStartIndex || json[structure.Key.Length + actualKeyIndex] != Structure.CharQuotationMark) // Recognize key with the same begin string but different ending { // out of order key recognized int keyEndIndex = json.IndexOf(Structure.CharQuotationMark, expectedKeyStartIndex); int?keyStructureIndex = null; StructureComplexOutOfOrder cachedOutOfOrderStruct = null; string actualKey = null; if (keyEndIndex != -1) // ignore if end of json is reached { actualKey = json.Substring(expectedKeyStartIndex, keyEndIndex - expectedKeyStartIndex); // check if out of order structure is already cached if (outOfOrderStructures != null) { for (int indexCachedOutOfOrder = 0; indexCachedOutOfOrder < outOfOrderStructures.Count; indexCachedOutOfOrder++) { var cachedItem = outOfOrderStructures[indexCachedOutOfOrder]; if (cachedItem.ObjectStructure[propIndex].Key == actualKey) { cachedOutOfOrderStruct = cachedItem; break; } } } if (cachedOutOfOrderStruct == null) { // find out of order key keyStructureIndex = FindOutOfOrderKey(objectStructure, actualKey); } } if (cachedOutOfOrderStruct != null) { structure = cachedOutOfOrderStruct.ObjectStructure[propIndex]; setPropertyAccessor = cachedOutOfOrderStruct.SetAccessorByPropertyIndex[propIndex]; } else if (keyStructureIndex.HasValue) { lock (syncObj) { // create or move out of order structure if (outOfOrderStructures == null) { outOfOrderStructures = new List <StructureComplexOutOfOrder>(); } // try reuse existing out of order array StructureComplexOutOfOrder currentOutOfOrderStruct; var existing = outOfOrderStructures.Where(ooo => ooo.MaxTargetIndex < propIndex).FirstOrDefault(); IJsonTypeStructure[] sourceStructure = objectStructure; Func <object, object>[] sourceGetAccessorByPropertyIndex = getAccessorByPropertyIndex; Action <object, object>[] sourceSetAccessorByPropertyIndex = setAccessorByPropertyIndex; // create new cloned out of order strucutre if (existing == null) { currentOutOfOrderStruct = new StructureComplexOutOfOrder() { ObjectStructure = (IJsonTypeStructure[])sourceStructure.Clone(), GetAccessorByPropertyIndex = (Func <object, object>[])sourceGetAccessorByPropertyIndex.Clone(), SetAccessorByPropertyIndex = (Action <object, object>[])sourceSetAccessorByPropertyIndex.Clone() }; } else { currentOutOfOrderStruct = existing; sourceStructure = existing.ObjectStructure; structure = sourceStructure[propIndex]; sourceGetAccessorByPropertyIndex = existing.GetAccessorByPropertyIndex; sourceSetAccessorByPropertyIndex = existing.SetAccessorByPropertyIndex; // search in chached object structure again keyStructureIndex = FindOutOfOrderKey(sourceStructure, actualKey); } // switch object structure to received json order IJsonTypeStructure foundOutOfOrderStructure = sourceStructure[keyStructureIndex.Value]; currentOutOfOrderStruct.ObjectStructure[propIndex] = foundOutOfOrderStructure; currentOutOfOrderStruct.ObjectStructure[keyStructureIndex.Value] = structure; structure = foundOutOfOrderStructure; currentOutOfOrderStruct.MaxTargetIndex = propIndex; // switch value setter var currentAccessor = sourceGetAccessorByPropertyIndex[propIndex]; var foundOutOfOrderAccessor = sourceGetAccessorByPropertyIndex[keyStructureIndex.Value]; currentOutOfOrderStruct.GetAccessorByPropertyIndex[propIndex] = foundOutOfOrderAccessor; currentOutOfOrderStruct.GetAccessorByPropertyIndex[keyStructureIndex.Value] = currentAccessor; var currentAccessorSet = sourceSetAccessorByPropertyIndex[propIndex]; var foundOutOfOrderAccessorSet = sourceSetAccessorByPropertyIndex[keyStructureIndex.Value]; currentOutOfOrderStruct.SetAccessorByPropertyIndex[propIndex] = foundOutOfOrderAccessorSet; currentOutOfOrderStruct.SetAccessorByPropertyIndex[keyStructureIndex.Value] = currentAccessorSet; setPropertyAccessor = foundOutOfOrderAccessorSet; if (existing == null) { outOfOrderStructures.Add(currentOutOfOrderStruct); } } } else { if (context.Serializer.IsMissingFieldDataAllowed) { if (SkipJsonElement(json, ref currentReadIndex)) { propIndex--; // re-read current structure } continue; // ignore missing field } else { throw new KeyNotFoundException(string.Format("The key: \"{0}\" is not present in the object structure! JSON: \"{1}\"; Target Type: \"{2}\"", actualKey, json, this.type.AssemblyQualifiedName)); } } } object value = structure.Deserialize(json, ref currentReadIndex, context); if (value != null) { setPropertyAccessor(target, value); } if (json[currentReadIndex] == Structure.CharRightBrace) { // end reached currentReadIndex++; break; } currentReadIndex++; } if (json[currentReadIndex - 1] != Structure.CharRightBrace) { if (context.Serializer.IsMissingFieldDataAllowed) { // json object contains more data than expected from the type structure // skip futher object data SkipJsonObject(json, ref currentReadIndex); } else { throw new InvalidOperationException(string.Format("The json object contains more data than expected in target type! JSON: \"{0}\"; Key: \"{1}\"; Target Type: \"{2}\"", json, this.key, this.type.AssemblyQualifiedName)); } } context.ParentObject = oldParentObj; return(target); } }
// ---------------------------------------------------------------------------------------- #endregion #region StructureArray properties // ---------------------------------------------------------------------------------------- // StructureArray properties // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- #endregion #region StructureArray methods // ---------------------------------------------------------------------------------------- // StructureArray methods // ---------------------------------------------------------------------------------------- /// <summary> /// Serializes the specified object. /// </summary> /// <param name="sb">The string builder.</param> /// <param name="obj">The object to serialize.</param> /// <param name="context">The context.</param> public override void Serialize(StringBuilder sb, object obj, SerializationContext context) { if (keyExpected) { sb.Append(Structure.QuotationMark); sb.Append(key); if (obj == null) { sb.Append(Structure.QuotationColonNullValue); return; } else { sb.Append(Structure.QuotationColonSeparator); } } if (isByteArray) { // Optimization: add byte array as base64 encoded string sb.Append(Structure.CharQuotationMark); byte[] data = (byte[])obj; sb.Append(Convert.ToBase64String(data)); sb.Append(Structure.CharQuotationMark); } else { sb.Append(Structure.CharLeftSquareBrace); //Type collType = obj.GetType(); // check if own collection implementation exposes special target type // todo: check if possible and required //bool specialTypeAdded = false; //if (!collType.IsArray // && !IsSystemCollection(collType)) // //&& !checkedSubInterfaceTypes.Contains(collType)) //{ // var exposureAttributes = collType.GetCustomAttributes(typeof(ExposeSubTypeAttribute), false); // if (exposureAttributes.Length > 0) // { // // expose specialized sub interface type // if (this.typeSerializerCache == null) // this.typeSerializerCache = new Dictionary<Type, IJsonTypeStructure>(); // // add typed collection attribute // sb.Append(Structure.TypeMetaTagJson); // sb.Append(this.type.FullName); // sb.Append(Structure.CharQuotationMark); // specialTypeAdded = true; // } // //checkedSubInterfaceTypes.Add(collType); //} IEnumerable collection = (IEnumerable)obj; int count = 0; foreach (var item in collection) { //if (specialTypeAdded // && count == 0) //{ // sb.Append(Structure.CharComma); //} if (item != null) { if (isObject) { // determine serialize type Type itemType = item.GetType(); IJsonTypeStructure currentObjectStructure; if (!typeSerializerCache.TryGetValue(itemType, out currentObjectStructure)) { Type targetType = null; if (unknownTypeResolver != null) { context.Key = this.key; context.ArrayIndex = count; targetType = unknownTypeResolver(context); context.ArrayIndex = null; } if (targetType == null) { targetType = itemType; } currentObjectStructure = Structure.DetermineStructure(targetType, GetNestedArrayKey(this.key, count), context, true); typeSerializerCache[itemType] = currentObjectStructure; } currentObjectStructure.Serialize(sb, item, context); } else { if (itemSerializer == null) { this.itemSerializer = Structure.DetermineStructure(itemType, key, context, true); } // use static serialize type itemSerializer.Serialize(sb, item, context); } } else { sb.Append(Structure.NullValue); } sb.Append(Structure.CharComma); count++; } if (count > 0) { sb.Length -= 1; // remove last comma } sb.Append(Structure.CharRightSquareBracet); } }
private object DeserializeArrayItem(string json, ref int currentReadIndex, int arrayItemIndex, SerializationContext context) { if (json[currentReadIndex] == 'n' && json[currentReadIndex + 1] == 'u' && json[currentReadIndex + 2] == 'l' && json[currentReadIndex + 3] == 'l') { currentReadIndex = currentReadIndex + 4; return(null); } //Type objectTargetType = null; if (json[currentReadIndex + 2] == '#' && json[currentReadIndex + 3] == 't' && json[currentReadIndex + 4] == 'y' && json[currentReadIndex + 5] == 'p' && json[currentReadIndex + 6] == 'e' ) { return(DeserializeSpecialType(json, ref currentReadIndex, context, currentReadIndex)); } object itemValue; if (this.isObject) { // determine target type during deserialization Type type = null; if (unknownTypeResolver != null) { context.Key = this.key; context.ValueStartIndex = currentReadIndex; context.ArrayIndex = arrayItemIndex; context.JsonString = json; type = unknownTypeResolver(context); context.ArrayIndex = null; } if (type == null || type.Equals(typeof(object))) { type = Structure.GetDefaultType(json, currentReadIndex); if (type == null) { int readCount = Math.Min(json.Length - currentReadIndex, 25); string jsonPart = json.Substring(currentReadIndex, readCount); throw new InvalidOperationException(string.Format("Unable to get the target type for array object index {0}; Key: {1}; JSON part: \"{2}\"", arrayItemIndex, this.key, jsonPart)); } } IJsonTypeStructure currentObjectStructure; if (!typeSerializerCache.TryGetValue(type, out currentObjectStructure)) { currentObjectStructure = Structure.DetermineStructure(type, GetNestedArrayKey(this.key, arrayItemIndex), context, true); typeSerializerCache[type] = currentObjectStructure; } itemValue = currentObjectStructure.Deserialize(json, ref currentReadIndex, context); } else { if (itemDeSerializer == null) { itemDeSerializer = Structure.DetermineStructure(itemType, key, context, true); } // fixed array target type itemValue = itemDeSerializer.Deserialize(json, ref currentReadIndex, context); } return(itemValue); }
// ---------------------------------------------------------------------------------------- #endregion #region StructureEnum constructors // ---------------------------------------------------------------------------------------- // StructureEnum constructors // ---------------------------------------------------------------------------------------- /// <summary> /// Creates a new instance of the <c>StructureEnum</c> class. /// </summary> public StructureEnum(string key, Type enumType, bool isArrayItem) : base(key, isArrayItem) { this.enumType = enumType; this.intEnumSerializer = new StructureInt(key, isArrayItem); }