/// <summary> /// Writes a json node to the internal buffer. /// </summary> /// <param name="jsonNavigator">The navigator to use to navigate the node</param> /// <param name="jsonNavigatorNode">The node to write.</param> public void WriteJsonNode(IJsonNavigator jsonNavigator, IJsonNavigatorNode jsonNavigatorNode) { if (jsonNavigator == null) { throw new ArgumentNullException($"{nameof(jsonNavigator)} can not be null"); } if (jsonNavigatorNode == null) { throw new ArgumentNullException($"{nameof(jsonNavigatorNode)} can not be null"); } // For now short circuit this to false until we figure out how to optimize this. bool sameFormat = jsonNavigator.SerializationFormat == this.SerializationFormat && (this.SerializationFormat == JsonSerializationFormat.Binary || this.SerializationFormat == JsonSerializationFormat.HybridRow); JsonNodeType jsonNodeType = jsonNavigator.GetNodeType(jsonNavigatorNode); // See if we can write the node without looking at it's value switch (jsonNodeType) { case JsonNodeType.Null: this.WriteNullValue(); return; case JsonNodeType.False: this.WriteBoolValue(false); return; case JsonNodeType.True: this.WriteBoolValue(true); return; } // If the navigator has the same format as this writer then we try to retrieve the node raw JSON IReadOnlyList <byte> bufferedRawJson; if (sameFormat && jsonNavigator.TryGetBufferedRawJson(jsonNavigatorNode, out bufferedRawJson)) { // Token type really doesn't make any difference other than whether this is a field name JsonTokenType jsonTokenType = (jsonNodeType == JsonNodeType.FieldName ? JsonTokenType.FieldName : JsonTokenType.Null); this.WriteRawJsonToken(jsonTokenType, bufferedRawJson); } else { // Either the formats did not match or we couldn't retrieve the buffered raw JSON switch (jsonNodeType) { case JsonNodeType.Number: double numberValue = jsonNavigator.GetNumberValue(jsonNavigatorNode); this.WriteNumberValue(numberValue); break; case JsonNodeType.String: case JsonNodeType.FieldName: bool fieldName = jsonNodeType == JsonNodeType.FieldName; IReadOnlyList <byte> bufferedStringValue; if (jsonNavigator.TryGetBufferedStringValue(jsonNavigatorNode, out bufferedStringValue)) { if (fieldName) { this.WriteRawJsonToken(JsonTokenType.FieldName, bufferedStringValue); } else { this.WriteRawJsonToken(JsonTokenType.String, bufferedStringValue); } } else { string value = jsonNavigator.GetStringValue(jsonNavigatorNode); if (fieldName) { this.WriteFieldName(value); } else { this.WriteStringValue(value); } } break; case JsonNodeType.Int8: { sbyte number = jsonNavigator.GetInt8Value(jsonNavigatorNode); this.WriteInt8Value(number); break; } case JsonNodeType.Int16: { short number = jsonNavigator.GetInt16Value(jsonNavigatorNode); this.WriteInt16Value(number); break; } case JsonNodeType.Int32: { int number = jsonNavigator.GetInt32Value(jsonNavigatorNode); this.WriteInt32Value(number); break; } case JsonNodeType.Int64: { long number = jsonNavigator.GetInt64Value(jsonNavigatorNode); this.WriteInt64Value(number); break; } case JsonNodeType.UInt32: { uint number = jsonNavigator.GetUInt32Value(jsonNavigatorNode); this.WriteUInt32Value(number); break; } case JsonNodeType.Float32: { float number = jsonNavigator.GetFloat32Value(jsonNavigatorNode); this.WriteFloat32Value(number); break; } case JsonNodeType.Float64: { double number = jsonNavigator.GetFloat64Value(jsonNavigatorNode); this.WriteFloat64Value(number); break; } case JsonNodeType.Guid: { Guid number = jsonNavigator.GetGuidValue(jsonNavigatorNode); this.WriteGuidValue(number); break; } case JsonNodeType.Binary: { IReadOnlyList <byte> bufferedBinaryValue; if (jsonNavigator.TryGetBufferedBinaryValue(jsonNavigatorNode, out bufferedBinaryValue)) { this.WriteRawJsonToken(JsonTokenType.Binary, bufferedBinaryValue); } else { IReadOnlyList <byte> value = jsonNavigator.GetBinaryValue(jsonNavigatorNode); this.WriteBinaryValue(value); } break; } case JsonNodeType.Array: this.WriteArrayStart(); foreach (IJsonNavigatorNode arrayItem in jsonNavigator.GetArrayItems(jsonNavigatorNode)) { this.WriteJsonNode(jsonNavigator, arrayItem); } this.WriteArrayEnd(); break; case JsonNodeType.Object: this.WriteObjectStart(); foreach (ObjectProperty objectProperty in jsonNavigator.GetObjectProperties(jsonNavigatorNode)) { this.WriteJsonNode(jsonNavigator, objectProperty.NameNode); this.WriteJsonNode(jsonNavigator, objectProperty.ValueNode); } this.WriteObjectEnd(); break; default: throw new ArgumentException($"Unexpected JsonNodeType: {jsonNodeType}"); } } }
/// <summary> /// Converts a list of CosmosElements into a memory stream. /// </summary> /// <param name="memoryStream">The memory stream response from Azure Cosmos</param> /// <param name="resourceType">The resource type</param> /// <param name="cosmosSerializationOptions">The custom serialization options. This allows custom serialization types like BSON, JSON, or other formats</param> /// <returns>Returns a memory stream of cosmos elements. By default the memory stream will contain JSON.</returns> internal static CosmosArray ToCosmosElements( MemoryStream memoryStream, ResourceType resourceType, CosmosSerializationFormatOptions cosmosSerializationOptions = null) { if (!memoryStream.CanRead) { throw new InvalidDataException("Stream can not be read"); } // Execute the callback an each element of the page // For example just could get a response like this // { // "_rid": "qHVdAImeKAQ=", // "Documents": [{ // "id": "03230", // "_rid": "qHVdAImeKAQBAAAAAAAAAA==", // "_self": "dbs\/qHVdAA==\/colls\/qHVdAImeKAQ=\/docs\/qHVdAImeKAQBAAAAAAAAAA==\/", // "_etag": "\"410000b0-0000-0000-0000-597916b00000\"", // "_attachments": "attachments\/", // "_ts": 1501107886 // }], // "_count": 1 // } // And you should execute the callback on each document in "Documents". long responseLengthBytes = memoryStream.Length; byte[] content = memoryStream.ToArray(); IJsonNavigator jsonNavigator = null; // Use the users custom navigator if (cosmosSerializationOptions != null) { jsonNavigator = cosmosSerializationOptions.CreateCustomNavigatorCallback(content); if (jsonNavigator == null) { throw new InvalidOperationException("The CosmosSerializationOptions did not return a JSON navigator."); } } else { jsonNavigator = JsonNavigator.Create(new ArraySegment <byte>(content)); } string resourceName = CosmosElementSerializer.GetRootNodeName(resourceType); CosmosArray documents; if ((jsonNavigator.SerializationFormat == JsonSerializationFormat.Binary) && jsonNavigator.TryGetObjectProperty( jsonNavigator.GetRootNode(), "stringDictionary", out ObjectProperty stringDictionaryProperty)) { // Payload is string dictionary encode so we have to decode using the string dictionary. IJsonNavigatorNode stringDictionaryNode = stringDictionaryProperty.ValueNode; JsonStringDictionary jsonStringDictionary = JsonStringDictionary.CreateFromStringArray( jsonNavigator .GetArrayItems(stringDictionaryNode) .Select(item => jsonNavigator.GetStringValue(item)) .ToList()); if (!jsonNavigator.TryGetObjectProperty( jsonNavigator.GetRootNode(), resourceName, out ObjectProperty resourceProperty)) { throw new InvalidOperationException($"Response Body Contract was violated. QueryResponse did not have property: {resourceName}"); } IJsonNavigatorNode resources = resourceProperty.ValueNode; if (!jsonNavigator.TryGetBufferedBinaryValue(resources, out ReadOnlyMemory <byte> resourceBinary)) { resourceBinary = jsonNavigator.GetBinaryValue(resources); } IJsonNavigator navigatorWithStringDictionary = JsonNavigator.Create(resourceBinary, jsonStringDictionary); if (!(CosmosElement.Dispatch( navigatorWithStringDictionary, navigatorWithStringDictionary.GetRootNode()) is CosmosArray cosmosArray)) { throw new InvalidOperationException($"QueryResponse did not have an array of : {resourceName}"); } documents = cosmosArray; } else { // Payload is not string dictionary encoded so we can just do for the documents as is. if (!jsonNavigator.TryGetObjectProperty( jsonNavigator.GetRootNode(), resourceName, out ObjectProperty objectProperty)) { throw new InvalidOperationException($"Response Body Contract was violated. QueryResponse did not have property: {resourceName}"); } if (!(CosmosElement.Dispatch( jsonNavigator, objectProperty.ValueNode) is CosmosArray cosmosArray)) { throw new InvalidOperationException($"QueryResponse did not have an array of : {resourceName}"); } documents = cosmosArray; } return(documents); }