public LazyCosmosArray( IJsonNavigator jsonNavigator, IJsonNavigatorNode jsonNavigatorNode) { JsonNodeType type = jsonNavigator.GetNodeType(jsonNavigatorNode); if (type != JsonNodeType.Array) { throw new ArgumentOutOfRangeException($"{nameof(jsonNavigatorNode)} must be an {JsonNodeType.Array} node. Got {type} instead."); } this.jsonNavigator = jsonNavigator; this.jsonNavigatorNode = jsonNavigatorNode; this.lazyCosmosElementArray = new Lazy <List <CosmosElement> >(() => { List <CosmosElement> elements = new List <CosmosElement>(jsonNavigator.GetArrayItemCount(jsonNavigatorNode)); // Using foreach instead of indexer, since the navigator doesn't support random seeks efficiently. foreach (IJsonNavigatorNode arrayItem in jsonNavigator.GetArrayItems(jsonNavigatorNode)) { elements.Add(CosmosElement.Dispatch(jsonNavigator, arrayItem)); } return(elements); }); }
private static JsonToken[] GetTokensFromArrayNode(IJsonNavigatorNode node, IJsonNavigator navigator, bool performCorrectnessCheck) { // Get tokens once through IEnumerable List <JsonToken> tokensFromIEnumerable = new List <JsonToken>(); IEnumerable <IJsonNavigatorNode> arrayItems = navigator.GetArrayItems(node); tokensFromIEnumerable.Add(JsonToken.ArrayStart()); foreach (IJsonNavigatorNode arrayItem in arrayItems) { tokensFromIEnumerable.AddRange(JsonNavigatorTests.GetTokensFromNode(arrayItem, navigator, performCorrectnessCheck)); } tokensFromIEnumerable.Add(JsonToken.ArrayEnd()); if (performCorrectnessCheck) { // Get tokens once again through indexer List <JsonToken> tokensFromIndexer = new List <JsonToken>(); tokensFromIndexer.Add(JsonToken.ArrayStart()); for (int i = 0; i < navigator.GetArrayItemCount(node); ++i) { tokensFromIndexer.AddRange(JsonNavigatorTests.GetTokensFromNode(navigator.GetArrayItemAt(node, i), navigator, performCorrectnessCheck)); } tokensFromIndexer.Add(JsonToken.ArrayEnd()); Assert.AreEqual(arrayItems.Count(), navigator.GetArrayItemCount(node)); Assert.IsTrue(tokensFromIEnumerable.SequenceEqual(tokensFromIndexer)); try { navigator.GetArrayItemAt(node, navigator.GetArrayItemCount(node) + 1); Assert.Fail("Expected to get an index out of range exception from going one past the end of the array."); } catch (IndexOutOfRangeException) { Assert.AreEqual(navigator.SerializationFormat, JsonSerializationFormat.Binary); } catch (ArgumentOutOfRangeException) { Assert.AreEqual(navigator.SerializationFormat, JsonSerializationFormat.Text); } } return(tokensFromIEnumerable.ToArray()); }
static void Navigate(IJsonNavigator navigator, IJsonNavigatorNode node) { switch (navigator.GetNodeType(node)) { case JsonNodeType.Null: case JsonNodeType.False: case JsonNodeType.True: case JsonNodeType.Number64: case JsonNodeType.Int8: case JsonNodeType.Int16: case JsonNodeType.Int32: case JsonNodeType.Int64: case JsonNodeType.UInt32: case JsonNodeType.Float32: case JsonNodeType.Float64: case JsonNodeType.String: case JsonNodeType.Binary: case JsonNodeType.Guid: break; case JsonNodeType.Array: foreach (IJsonNavigatorNode arrayItem in navigator.GetArrayItems(node)) { Navigate(navigator, arrayItem); } break; case JsonNodeType.Object: foreach (ObjectProperty objectProperty in navigator.GetObjectProperties(node)) { IJsonNavigatorNode nameNode = objectProperty.NameNode; IJsonNavigatorNode valueNode = objectProperty.ValueNode; Navigate(navigator, valueNode); } break; default: throw new ArgumentOutOfRangeException($"Unknown {nameof(JsonNodeType)}: '{navigator.GetNodeType(node)}.'"); } }
/// <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); }