private object ReadValue( ref Utf8JsonReader reader, IOpenApiType valueType, Type valueClrType, JsonSerializerOptions options) { // TODO: dictionary and enumerable handling if (!reader.Read()) { throw new JsonException($"Unexpected end of stream."); } if (reader.TokenType == JsonTokenType.Null) { return(null); } if (valueClrType == typeof(string)) { return(reader.GetString()); } if (ReflectionHelper.ImplementsEnumerable(valueClrType, out var itemType)) { var array = ReadArray(ref reader, _typeHandler.ResolveType(itemType), itemType, options); // TODO: convert to actual target type return(array); } else if (ReflectionHelper.ImplementsDictionary(valueClrType, out var dictKeyType, out var dictValueType)) { // TODO: dictionarySupport return(null); }
public static object ReadObject( JsonElement obj, IOpenApiType itemType, Type itemClrType, IOpenApiTypeHandler typeHandler, bool objectAsDelta = false) { if (itemType == null) { throw new SerializationException("Cannot deserialize unknown type."); } var properties = obj.EnumerateObject().ToDictionary(p => p.Name, p => p.Value); if (properties.TryGetValue("@odata.type", out var type) && type.ValueKind == JsonValueKind.String) { var typeName = type.GetString(); var actualType = typeHandler.ResolveType(typeName); if (actualType == null) { throw new SerializationException($"No type with identifier '{typeName}' found"); } if (!itemClrType.IsAssignableFrom(actualType.ClrType)) { throw new SerializationException($"Type '{typeName}' is not assignable to '{itemType.JsonName}'"); } itemType = actualType; itemClrType = actualType.ClrType; } var instance = objectAsDelta ? Activator.CreateInstance(typeof(Delta <>).MakeGenericType(itemClrType)) : Activator.CreateInstance(itemClrType); foreach (var member in properties) { if (itemType.TryGetProperty(member.Key, out var property)) { var propertyType = typeHandler.ResolveType(property.ClrProperty.PropertyType); var value = ReadValue(member.Value, propertyType, property.ClrProperty.PropertyType, typeHandler, objectAsDelta); // TODO: check for components in the model binding area which help us here // many conversions (string -> enum,date,timespan) etc. do not work like this. property.SetValue(instance, value); } else { // TODO: how do we want to treat unknown props? fail or ignore? } } return(instance); }
public bool Equals(IOpenApiType other) { if (ReferenceEquals(null, other)) { return(false); } if (ReferenceEquals(this, other)) { return(true); } return(Equals(ClrType, other.ClrType)); }
private object ReadArray( ref Utf8JsonReader reader, IOpenApiType itemType, Type itemClrType, JsonSerializerOptions options) { if (!reader.Read()) { throw new JsonException($"Unexpected end of stream."); } switch (reader.TokenType) { case JsonTokenType.Null: return(null); case JsonTokenType.StartObject: return(ReadObjectArray(ref reader, itemType, itemClrType, options)); case JsonTokenType.StartArray: if (itemClrType.IsArray) { var elementType = itemClrType.GetElementType(); return(ReadArrayArray(ref reader, _typeHandler.ResolveType(elementType), elementType, options)); } else { throw new JsonException($"Unexpected JSON Token {reader.TokenType}."); } case JsonTokenType.String: case JsonTokenType.Number: case JsonTokenType.True: case JsonTokenType.False: // TODO: read array even though we already started it? we might need to do reading on our own // simple arrays return(JsonSerializer.Deserialize(ref reader, itemClrType.MakeArrayType(), options)); case JsonTokenType.EndArray: return(Array.CreateInstance(itemClrType, 0)); default: throw new JsonException($"Unexpected JSON Token {reader.TokenType}."); } }
public static void WriteArray( Utf8JsonWriter writer, IEnumerable resultItems, IOpenApiType itemType, SelectClause selectClause, IOpenApiTypeHandler typeHandler, JsonSerializerOptions options) { writer.WriteStartArray(); foreach (var item in resultItems) { WriteValue(writer, itemType, item, selectClause, typeHandler, options); } writer.WriteEndArray(); }
public static void WriteObject( Utf8JsonWriter writer, IOpenApiType itemType, object item, SelectClause selectClause, IOpenApiTypeHandler typeHandler, JsonSerializerOptions options) { if (item == null) { writer.WriteNullValue(); } else { writer.WriteStartObject(); var actualType = typeHandler.ResolveType(item.GetType()); var needsType = itemType == null || !itemType.Equals(actualType); if (needsType) { writer.WriteString("@odata.type", actualType.JsonName); } foreach (var property in actualType.Properties) { SelectClause subClause = null; if (selectClause == null || selectClause.SelectClauses?.TryGetValue(property.ClrProperty, out subClause) == true || selectClause.IsStarSelect) { var key = options.PropertyNamingPolicy.ConvertName(property.JsonName); writer.WritePropertyName(JsonEncodedText.Encode(key)); WriteValue(writer, typeHandler.ResolveType(property.ClrProperty.PropertyType), property.GetValue(item), subClause, typeHandler, options); } } writer.WriteEndObject(); } }
public static object ReadValue( JsonElement value, IOpenApiType valueType, Type valueClrType, IOpenApiTypeHandler typeHandler, bool objectsAsDelta = false) { switch (value.ValueKind) { case JsonValueKind.Object: if (valueType == null && ReflectionHelper.ImplementsDictionary(valueClrType, out var dictKeyType, out var dictValueType)) { var dictionary = Activator.CreateInstance(valueClrType); var indexer = valueClrType.GetProperties() .FirstOrDefault(p => IsDictionaryIndexer(p, dictKeyType, dictValueType)); if (indexer == null) { throw new SerializationException( "Could not find dictionary indexer for deserializing object"); } var dictValueApiType = typeHandler.ResolveType(dictValueType); foreach (var prop in value.EnumerateObject()) { var dictValue = ReadValue(prop.Value, dictValueApiType, dictValueType, typeHandler, false); indexer.SetValue(dictionary, dictValue, new object[] { prop.Name }); } return(dictionary); } else { return(ReadObject(value, valueType, valueClrType, typeHandler, objectsAsDelta)); }
public Type GetType(IOpenApiObjectToTypeService objectService, IObjectsProcessingKeyStore objectKeyStore, IOpenApiType openApiType, IDictionary <string, IOpenApiType> definitions, string suggestedObjectName = null) { if (openApiType is OpenApiPrimitiveType) { return(_primitiveService.GetType((OpenApiPrimitiveType)openApiType, nameIfEnum: suggestedObjectName)); } if (openApiType.Matches <OpenApiReferencedType>()) { return(_referenceService.GetType(objectService, typeResolver: this, objectKeyStore, (OpenApiReferencedType)openApiType, definitions)); } if (openApiType.Matches <OpenApiObjectType>()) { return(objectService.GetType((OpenApiObjectType)openApiType, definitions, suggestedObjectName, objectKeyStore)); } if (openApiType.Matches <OpenApiArrayType>()) { var memberType = ((OpenApiArrayType)openApiType).Items; var arrayMemberType = GetType(objectService, objectKeyStore, memberType, definitions, suggestedObjectName + "Member"); return(typeof(IEnumerable <>).MakeGenericType(arrayMemberType)); } throw new NotSupportedException($"Unable to convert unsupported " + $"OpenApi Type {openApiType.GetType()}"); }
private object ReadObject( ref Utf8JsonReader reader, IOpenApiType itemType, Type itemClrType, JsonSerializerOptions options) { if (itemType == null) { throw new JsonException($"Cannot deserialize unknown type."); } // TODO: better object creation, and polymorphism handling! var instance = Activator.CreateInstance(itemClrType); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndObject) { return(instance); } if (reader.TokenType != JsonTokenType.PropertyName) { throw new JsonException($"Unexpected JSON Token {reader.TokenType}."); } var propertyName = reader.GetString(); if (itemType.TryGetProperty(propertyName, out var property)) { var propertyType = _typeHandler.ResolveType(property.ClrProperty.PropertyType); var value = ReadValue(ref reader, propertyType, property.ClrProperty.PropertyType, options); property.SetValue(instance, value); } else { throw new JsonException($"Unexpected property {propertyName}."); } } throw new JsonException($"Unexpected end of stream."); }
private object ReadArrayArray( ref Utf8JsonReader reader, IOpenApiType itemType, Type itemClrType, JsonSerializerOptions options) { var resultItems = (IList)Activator.CreateInstance(typeof(List <>).MakeGenericType(itemClrType)); do { if (reader.TokenType == JsonTokenType.EndArray) { var array = Array.CreateInstance(itemClrType, resultItems.Count); resultItems.CopyTo(array, 0); return(array); } resultItems.Add(ReadArray(ref reader, itemType, itemClrType, options)); } while (reader.Read()); throw new JsonException($"Unexpected end of stream."); }
public static void WriteDictionary( Utf8JsonWriter writer, IDictionary value, IOpenApiType valueType, IOpenApiTypeHandler typeHandler, JsonSerializerOptions options) { writer.WriteStartObject(); foreach (DictionaryEntry dictionaryEntry in value) { var key = options.DictionaryKeyPolicy != null ? options.DictionaryKeyPolicy.ConvertName(dictionaryEntry.Key.ToString()) : dictionaryEntry.Key.ToString(); writer.WritePropertyName(JsonEncodedText.Encode(key)); WriteValue(writer, valueType, dictionaryEntry.Value, null, typeHandler, options); } writer.WriteEndObject(); }
public static void WriteValue( Utf8JsonWriter writer, IOpenApiType itemType, object item, SelectClause subClause, IOpenApiTypeHandler typeHandler, JsonSerializerOptions options) { switch (item) { // we must handle dictionaries, enumerables etc on our own for proper nested object handling // maybe we find a better way to utilize the JsonSerializer.Serialize directly without loosing the proper type handling case string s: // string is an IEnumerable and needs to be handled special to avoid serialization as array writer.WriteStringValue(s); break; case Delta d: JsonSerializer.Serialize(writer, d, d.GetType(), options); break; case IDictionary c: if (ReflectionHelper.ImplementsDictionary(c.GetType(), out _, out var valueType)) { WriteDictionary(writer, c, typeHandler.ResolveType(valueType), // use static types for polymorphism typeHandler, options); } else { WriteDictionary(writer, c, null, typeHandler, options); } break; case IEnumerable v: if (ReflectionHelper.ImplementsEnumerable(v.GetType(), out var enumerableItemType)) { WriteArray(writer, v, typeHandler.ResolveType(enumerableItemType), subClause, typeHandler, options); } else { WriteArray(writer, v, null, subClause, typeHandler, options); } break; default: if (itemType == null) { // not handled by OpenApiQuery JsonSerializer.Serialize(writer, item, options); } else { WriteObject(writer, itemType, item, subClause, typeHandler, options); } break; } }