< Summary

Class:Microsoft.Azure.Search.Serialization.JsonExtensions
Assembly:Microsoft.Azure.Search.Common
File(s):C:\Git\azure-sdk-for-net\sdk\search\Microsoft.Azure.Search.Common\src\Customizations\Search\Serialization\JsonExtensions.cs
Covered lines:55
Uncovered lines:8
Coverable lines:63
Total lines:288
Line coverage:87.3% (55 of 63)
Covered branches:34
Total branches:38
Branch coverage:89.4% (34 of 38)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
ExpectAndAdvance(...)-100%100%
ExpectAndAdvance(...)-100%100%
Expect(...)-0%100%
Expect(...)-73.91%92.86%
Advance(...)-75%50%
ReadObject(...)-100%100%
ReadObjectAndAdvance(...)-100%100%
ReadObject(...)-100%100%
IsString(...)-100%83.33%
IsNumber(...)-100%83.33%
IsValid(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\search\Microsoft.Azure.Search.Common\src\Customizations\Search\Serialization\JsonExtensions.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License. See License.txt in the project root for
 3// license information.
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Linq;
 8using System.Reflection;
 9using Microsoft.Azure.Search.Common;
 10using Newtonsoft.Json;
 11using Newtonsoft.Json.Linq;
 12
 13namespace Microsoft.Azure.Search.Serialization
 14{
 15    /// <summary>
 16    /// Defines extension methods for various JSON.NET types that make it easier to implement a custom JsonConverter.
 17    /// </summary>
 18    public static class JsonExtensions
 19    {
 20        /// <summary>
 21        /// Asserts that the given JSON reader is positioned on a token with the expected type. Optionally asserts
 22        /// that the value of the token matches a given expected value. If any of the assertions fail, this method
 23        /// throws a JsonSerializationException. Otherwise, this method attempts to advance the JSON reader to the
 24        /// next position.
 25        /// </summary>
 26        /// <param name="reader">The JSON reader.</param>
 27        /// <param name="expectedToken">The JSON token on which the reader is expected to be positioned.</param>
 28        /// <param name="expectedValues">Optional; The expected possible values of the current JSON token.</param>
 29        public static void ExpectAndAdvance(
 30            this JsonReader reader,
 31            JsonToken expectedToken,
 381432            params object[] expectedValues) => ExpectAndAdvance<object>(reader, expectedToken, expectedValues);
 33
 34        /// <summary>
 35        /// Asserts that the given JSON reader is positioned on a token with the expected type and retrieves the
 36        /// value of the token, if any. Optionally asserts that the value of the token matches a given expected
 37        /// value. If any of the assertions fail, this method throws a JsonSerializationException. Otherwise, this
 38        /// method attempts to advance the JSON reader to the next position.
 39        /// </summary>
 40        /// <typeparam name="TValue">The expected type of the value of the current JSON token.</typeparam>
 41        /// <param name="reader">The JSON reader.</param>
 42        /// <param name="expectedToken">The JSON token on which the reader is expected to be positioned.</param>
 43        /// <param name="expectedValues">Optional; The expected possible values of the current JSON token.</param>
 44        /// <returns>
 45        /// The value of the JSON token before advancing the reader, or default(TValue) if the token has no value.
 46        /// </returns>
 47        public static TValue ExpectAndAdvance<TValue>(
 48            this JsonReader reader,
 49            JsonToken expectedToken,
 50            params object[] expectedValues)
 51        {
 772652            TValue result = Expect<TValue>(reader, expectedToken, expectedValues);
 771653            Advance(reader);
 771654            return result;
 55        }
 56
 57        /// <summary>
 58        /// Asserts that the given JSON reader is positioned on a token with the expected type. Optionally asserts
 59        /// that the value of the token matches a given expected value. If any of the assertions fail, this method
 60        /// throws a JsonSerializationException.
 61        /// </summary>
 62        /// <param name="reader">The JSON reader.</param>
 63        /// <param name="expectedToken">The JSON token on which the reader is expected to be positioned.</param>
 64        /// <param name="expectedValues">Optional; The expected possible values of the current JSON token.</param>
 65        public static void Expect(this JsonReader reader, JsonToken expectedToken, params object[] expectedValues) =>
 066            Expect<object>(reader, expectedToken, expectedValues);
 67
 68        /// <summary>
 69        /// Asserts that the given JSON reader is positioned on a token with the expected type and retrieves the
 70        /// value of the token, if any. Optionally asserts that the value of the token matches a given expected
 71        /// value. If any of the assertions fail, this method throws a JsonSerializationException.
 72        /// </summary>
 73        /// <typeparam name="TValue">The expected type of the value of the current JSON token.</typeparam>
 74        /// <param name="reader">The JSON reader.</param>
 75        /// <param name="expectedToken">The JSON token on which the reader is expected to be positioned.</param>
 76        /// <param name="expectedValues">Optional; The expected possible values of the current JSON token.</param>
 77        /// <returns>
 78        /// The value of the current JSON token, or default(TValue) if the current token has no value.
 79        /// </returns>
 80        public static TValue Expect<TValue>(
 81            this JsonReader reader,
 82            JsonToken expectedToken,
 83            params object[] expectedValues)
 84        {
 2464285            Throw.IfArgumentNull(reader, nameof(reader));
 86
 2464287            if (reader.TokenType != expectedToken)
 88            {
 289                throw new JsonSerializationException(
 290                    string.Format("Deserialization failed. Expected token: '{0}'", expectedToken));
 91            }
 92
 2464093            if (expectedValues != null && expectedValues.Length > 0 &&
 3072294                (reader.Value == null || !Array.Exists(expectedValues, v => reader.Value.Equals(v))))
 95            {
 896                string message =
 897                    string.Format(
 898                        "Deserialization failed. Expected value(s): '{0}'. Actual: '{1}'",
 899                        string.Join(", ", expectedValues),
 8100                        reader.Value);
 101
 8102                throw new JsonSerializationException(message);
 103            }
 104
 24632105            var result = default(TValue);
 106
 24632107            if (reader.Value != null)
 108            {
 22222109                if (!typeof(TValue).GetTypeInfo().IsAssignableFrom(reader.ValueType.GetTypeInfo()))
 110                {
 0111                    string message =
 0112                        string.Format(
 0113                            "Deserialization failed. Value '{0}' is not of expected type '{1}'.",
 0114                            reader.Value,
 0115                            typeof(TValue));
 116
 0117                    throw new JsonSerializationException(message);
 118                }
 119
 22222120                result = (TValue)reader.Value;
 121            }
 122
 24632123            return result;
 124        }
 125
 126        /// <summary>
 127        /// Advances the given JSON reader, or throws a JsonSerializationException if it cannot be advanced.
 128        /// </summary>
 129        /// <param name="reader">The JSON reader to advance.</param>
 130        public static void Advance(this JsonReader reader)
 131        {
 8766132            Throw.IfArgumentNull(reader, nameof(reader));
 133
 8766134            if (!reader.Read())
 135            {
 0136                throw new JsonSerializationException("Deserialization failed. Unexpected end of input.");
 137            }
 8766138        }
 139
 140        /// <summary>
 141        /// Reads the properties of JSON objects, enforcing the presence of required properties and ignoring the order o
 142        /// </summary>
 143        /// <param name="reader">The JSON reader to use to read an object.</param>
 144        /// <param name="requiredProperties">
 145        /// The names of all JSON properties that are expected to be present in the parsed object.
 146        /// </param>
 147        /// <param name="readProperty">
 148        /// A callback that reads a property value with the given name from the given <c cref="JsonReader">JsonReader</c
 149        /// advance the reader to the name of the next property, or the end of the object if there are no more propertie
 150        /// </param>
 151        /// <remarks>
 152        /// This method will leave the reader positioned on the end of the object.
 153        /// </remarks>
 154        public static void ReadObject(
 155            this JsonReader reader,
 156            IEnumerable<string> requiredProperties,
 157            Action<JsonReader, string> readProperty) =>
 920158            reader.ReadObject(requiredProperties, Enumerable.Empty<string>(), readProperty);
 159
 160        /// <summary>
 161        /// Reads the properties of JSON objects, enforcing the presence of required properties and ignoring the order o
 162        /// and then advances the given reader to the next token after the end of the object.
 163        /// </summary>
 164        /// <param name="reader">The JSON reader to use to read an object.</param>
 165        /// <param name="requiredProperties">
 166        /// The names of all JSON properties that are expected to be present in the parsed object.
 167        /// </param>
 168        /// <param name="readProperty">
 169        /// A callback that reads a property value with the given name from the given <c cref="JsonReader">JsonReader</c
 170        /// advance the reader to the name of the next property, or the end of the object if there are no more propertie
 171        /// </param>
 172        /// <remarks>
 173        /// This method will advance the reader to the next position after the end of the object.
 174        /// </remarks>
 175        public static void ReadObjectAndAdvance(
 176            this JsonReader reader,
 177            IEnumerable<string> requiredProperties,
 178            Action<JsonReader, string> readProperty)
 179        {
 920180            reader.ReadObject(requiredProperties, readProperty);
 914181            reader.Advance();
 914182        }
 183
 184        /// <summary>
 185        /// Reads the properties of JSON objects, enforcing the presence of required properties and ignoring the order o
 186        /// </summary>
 187        /// <param name="reader">The JSON reader to use to read an object.</param>
 188        /// <param name="requiredProperties">
 189        /// The names of all JSON properties that are expected to be present in the parsed object.
 190        /// </param>
 191        /// <param name="optionalProperties">
 192        /// The names of JSON properties besides the required properties that may be present in the parsed object.
 193        /// </param>
 194        /// <param name="readProperty">
 195        /// A callback that reads a property value with the given name from the given <c cref="JsonReader">JsonReader</c
 196        /// advance the reader to the name of the next property, or the end of the object if there are no more propertie
 197        /// </param>
 198        /// <remarks>
 199        /// This method will leave the reader positioned on the end of the object.
 200        /// </remarks>
 201        public static void ReadObject(
 202            this JsonReader reader,
 203            IEnumerable<string> requiredProperties,
 204            IEnumerable<string> optionalProperties,
 205            Action<JsonReader, string> readProperty)
 206        {
 1406207            Throw.IfArgumentNull(requiredProperties, nameof(requiredProperties));
 1406208            Throw.IfArgumentNull(optionalProperties, nameof(optionalProperties));
 1406209            Throw.IfArgumentNull(readProperty, nameof(readProperty));
 210
 211            // ExpectAndAdvance validates that reader is not null.
 1406212            reader.ExpectAndAdvance(JsonToken.StartObject);
 213
 1406214            string[] allPropertyNames = requiredProperties.Concat(optionalProperties).ToArray();
 1406215            var processedProperties = new HashSet<string>();
 216
 4202217            while (reader.TokenType != JsonToken.EndObject)
 218            {
 2812219                string propertyName = reader.ExpectAndAdvance<string>(JsonToken.PropertyName, allPropertyNames);
 2806220                readProperty(reader, propertyName);
 2796221                processedProperties.Add(propertyName);
 222            }
 223
 7418224            foreach (var propertyName in requiredProperties)
 225            {
 2320226                if (!processedProperties.Contains(propertyName))
 227                {
 2228                    throw new JsonSerializationException(
 2229                        string.Format("Deserialization failed. Could not find required '{0}' property.", propertyName));
 230                }
 231            }
 1388232        }
 233
 234        /// <summary>
 235        /// Indicates whether or not the given JSON token matches the expected string.
 236        /// </summary>
 237        /// <param name="token">The token to check.</param>
 238        /// <param name="expectedValue">The expected string value.</param>
 239        /// <returns><c>true</c> if the given JSON token matches the expected string, <c>false</c> otherwise.</returns>
 240        public static bool IsString(this JToken token, string expectedValue) =>
 362241            token?.Type == JTokenType.String && token?.Value<string>() == expectedValue;
 242
 243        /// <summary>
 244        /// Indicates whether or not the given JSON token is a numeric literal.
 245        /// </summary>
 246        /// <param name="token">The token to check.</param>
 247        /// <returns><c>true</c> if the given JSON token represents a number, <c>false</c> otherwise.</returns>
 268248        public static bool IsNumber(this JToken token) => token?.Type == JTokenType.Float || token?.Type == JTokenType.I
 249
 250        /// <summary>
 251        /// Validates the properties of the given JSON object, enforcing the presence of required properties and ignorin
 252        /// the order of properties.
 253        /// </summary>
 254        /// <param name="obj">The JSON object to validate.</param>
 255        /// <param name="requiredProperties">
 256        /// The names of all JSON properties that are expected to be present in the given object.
 257        /// </param>
 258        /// <param name="isPropertyValid">
 259        /// A predicate that determines whether the name and value of given <c cref="JProperty">JProperty</c> are valid.
 260        /// </param>
 261        /// <returns>
 262        /// <c>true</c> if all properties of the given JSON object pass the given validation function and all required p
 263        /// <c>false</c> otherwise.
 264        /// </returns>
 265        public static bool IsValid(this JObject obj, IEnumerable<string> requiredProperties, Func<JProperty, bool> isPro
 266        {
 538267            Throw.IfArgumentNull(obj, nameof(obj));
 538268            Throw.IfArgumentNull(requiredProperties, nameof(requiredProperties));
 538269            Throw.IfArgumentNull(isPropertyValid, nameof(isPropertyValid));
 270
 538271            var processedProperties = new HashSet<string>();
 272
 2686273            foreach (JProperty property in obj.Properties())
 274            {
 900275                if (isPropertyValid(property))
 276                {
 710277                    processedProperties.Add(property.Name);
 278                }
 279                else
 280                {
 190281                    return false;
 282                }
 283            }
 284
 932285            return requiredProperties.All(p => processedProperties.Contains(p));
 190286        }
 287    }
 288}