/// <summary> /// Read the @search.facets property value. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="results">The SearchResults to add the facets to.</param> /// <param name="options">JSON serializer options.</param> private static void ReadFacets( ref Utf8JsonReader reader, SearchResults <T> results, JsonSerializerOptions options) { Debug.Assert(results != null); // Facets are optional, so short circuit if nothing is found if (reader.TokenType == JsonTokenType.Null) { return; } results.Facets = new Dictionary <string, IList <FacetResult> >(); reader.Expects(JsonTokenType.StartObject); while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { // Get the name of the facet string facetName = reader.ExpectsPropertyName(); // Get the values of the facet List <FacetResult> facets = new List <FacetResult>(); reader.Expects(JsonTokenType.StartArray); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { FacetResult facet = ReadFacetResult(ref reader, options); facets.Add(facet); } // Add the facet to the results results.Facets[facetName] = facets; } }
/// <summary> /// Parse the SearchResults and its search results. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="typeToConvert">The type to parse into.</param> /// <param name="options">Serialization options.</param> /// <returns>The deserialized search results.</returns> public override SearchResults <T> Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { Debug.Assert(typeToConvert != null); Debug.Assert(typeToConvert.IsAssignableFrom(typeof(SearchResults <T>))); Debug.Assert(options != null); SearchResults <T> results = new SearchResults <T>(); reader.Expects(JsonTokenType.StartObject); while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { switch (reader.ExpectsPropertyName()) { case Constants.ODataCountKey: results.TotalCount = reader.ExpectsNullableLong(); break; case Constants.SearchCoverageKey: results.Coverage = reader.ExpectsNullableDouble(); break; case Constants.SearchFacetsKey: ReadFacets(ref reader, results, options); break; case Constants.ODataNextLinkKey: results.NextUri = new Uri(reader.ExpectsString()); break; case Constants.SearchNextPageKey: results.NextOptions = ReadNextPageOptions(ref reader); break; case Constants.ValueKey: reader.Expects(JsonTokenType.StartArray); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { SearchResult <T> result = _resultConverter.Read(ref reader, _resultType, options); results.Values.Add(result); } break; default: // Ignore other properties (including OData context, etc.) reader.Skip(); break; } } return(results); }
/// <summary> /// Parse a SearchSuggestion and its model. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="typeToConvert">The type to parse into.</param> /// <param name="options">Serialization options.</param> /// <returns>The deserialized suggestion.</returns> public override SearchSuggestion <T> Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { Debug.Assert(options != null); SearchSuggestion <T> suggestion = new SearchSuggestion <T>(); // Clone the reader so we can get the search text property without // advancing the reader over any properties needed to deserialize // the user's model type. Utf8JsonReader clone = reader; clone.Expects(JsonTokenType.StartObject); while (clone.Read() && clone.TokenType != JsonTokenType.EndObject) { string name = clone.ExpectsPropertyName(); if (name == Constants.SearchTextKey) { suggestion.Text = clone.ExpectsString(); break; } } // Deserialize the model T document = JsonSerializer.Deserialize <T>(ref reader, options); suggestion.Document = document; return(suggestion); }
/// <summary> /// Read the @search.highlights property value. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="result">The SearchResult to add the highlights to.</param> private static void ReadHighlights(ref Utf8JsonReader reader, SearchResult <T> result) { Debug.Assert(result != null); result.Highlights = new Dictionary <string, IList <string> >(); reader.Expects(JsonTokenType.StartObject); while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { // Get the highlight field name string name = reader.ExpectsPropertyName(); // Get the highlight values List <string> values = new List <string>(); reader.Expects(JsonTokenType.StartArray); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { values.Add(reader.ExpectsString()); } // Add the highlight result.Highlights[name] = values; } }
/// <summary> /// Parse the SuggestResults and its suggestions. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="typeToConvert">The type to parse into.</param> /// <param name="options">Serialization options.</param> /// <returns>The deserialized suggestions.</returns> public override SuggestResults <T> Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { Debug.Assert(typeToConvert != null); Debug.Assert(typeToConvert.IsAssignableFrom(typeof(SuggestResults <T>))); Debug.Assert(options != null); SuggestResults <T> suggestions = new SuggestResults <T>(); reader.Expects(JsonTokenType.StartObject); while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { switch (reader.ExpectsPropertyName()) { case Constants.SearchCoverageKey: suggestions.Coverage = reader.ExpectsNullableDouble(); break; case Constants.ValueKey: reader.Expects(JsonTokenType.StartArray); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { SearchSuggestion <T> suggestion = _suggestionConverter.Read(ref reader, _suggestionType, options); suggestions.Results.Add(suggestion); } break; default: // Ignore other properties (including OData context, etc.) reader.Skip(); break; } } return(suggestions); }
/// <summary> /// Parse a SearchResult and its model. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="typeToConvert">The type to parse into.</param> /// <param name="options">Serialization options.</param> /// <returns>The deserialized search result.</returns> public override SearchResult <T> Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { Debug.Assert(options != null); SearchResult <T> result = new SearchResult <T>(); // Clone the reader so we can get the search text property without // advancing the reader over any properties needed to deserialize // the user's model type. Utf8JsonReader clone = reader; // Keep track of the properties we've found so we can stop reading // through the cloned object bool parsedScore = false; bool parsedHighlights = false; clone.Expects(JsonTokenType.StartObject); while ( (!parsedScore || !parsedHighlights) && clone.Read() && clone.TokenType != JsonTokenType.EndObject) { string name = clone.ExpectsPropertyName(); if (name == Constants.SearchScoreKey) { parsedScore = true; result.Score = clone.ExpectsNullableDouble(); } else if (name == Constants.SearchHighlightsKey) { parsedHighlights = true; ReadHighlights(ref clone, result); } else { // Skip the rest of the next property's value clone.Skip(); } } // Deserialize the model T document = JsonSerializer.Deserialize <T>(ref reader, options); result.Document = document; return(result); }
/// <summary> /// Read the facet result value. /// </summary> /// <param name="reader">JSON reader.</param> /// <param name="options">Serializer options.</param> /// <returns>The facet result.</returns> private static FacetResult ReadFacetResult( ref Utf8JsonReader reader, JsonSerializerOptions options) { FacetResult facet = new FacetResult(); reader.Expects(JsonTokenType.StartObject); while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { // Get the name of the facet property string facetName = reader.ExpectsPropertyName(); if (facetName == Constants.CountKey) { facet.Count = reader.ExpectsNullableLong(); } else { object value = reader.ReadObject(options); facet[facetName] = value; } } return(facet); }
/// <summary> /// Parse JSON into a SearchDocument. /// </summary> /// <param name="reader">The JSON reader.</param> /// <param name="typeToConvert">The type to convert to.</param> /// <param name="options">Serialization options.</param> /// <param name="recursionDepth"> /// Depth of the current read recursion to bail out of circular /// references. /// </param> /// <returns>A deserialized SearchDocument.</returns> public static SearchDocument ReadSearchDocument( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options, int?recursionDepth = null) { Debug.Assert(typeToConvert != null); Debug.Assert(typeToConvert.IsAssignableFrom(typeof(SearchDocument))); recursionDepth ??= options.GetMaxRecursionDepth(); AssertRecursionDepth(recursionDepth.Value); SearchDocument doc = new SearchDocument(); reader.Expects(JsonTokenType.StartObject); while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { string propertyName = reader.ExpectsPropertyName(); // Ignore OData properties - we don't expose those on custom // user schemas if (!propertyName.StartsWith(Constants.ODataKeyPrefix, StringComparison.OrdinalIgnoreCase)) { object value = ReadSearchDocObject(ref reader, recursionDepth.Value - 1); doc[propertyName] = value; } else { reader.Skip(); } } return(doc); // TODO: #10596 - Investigate using JsonSerializer for reading SearchDocument properties // The built in converters for JsonSerializer are a little more // helpful than we want right now and will do things like turn "1" // to the integer 1 instead of a string. The number of special // cases needed for converting dynamic documents is small enough // that we're hard coding them here for now. We'll revisit with // Search experts and their customer scenarios to get this right in // the next preview. object ReadSearchDocObject(ref Utf8JsonReader reader, int depth) { AssertRecursionDepth(depth); switch (reader.TokenType) { case JsonTokenType.String: case JsonTokenType.Number: case JsonTokenType.True: case JsonTokenType.False: case JsonTokenType.None: case JsonTokenType.Null: return(ReadPrimitiveValue(ref reader)); case JsonTokenType.StartObject: // TODO: #10592- Unify on an Azure.Core spatial type // Return a complex object return(ReadSearchDocument(ref reader, typeof(SearchDocument), options, depth - 1)); case JsonTokenType.StartArray: var list = new List <object>(); while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) { recursionDepth--; object value = ReadSearchDocObject(ref reader, depth - 1); list.Add(value); } return(list.ToArray()); default: throw new JsonException(); } } }