// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information. using System.Collections.Generic; using System.Dynamic; using System.Runtime.Serialization.Json; using Xunit; namespace System.Json { public class JsonValueDynamicTest { const string InvalidIndexType = "Invalid '{0}' index type; only 'System.String' and non-negative 'System.Int32' types are supported."; const string NonSingleNonNullIndexNotSupported = "Null index or multidimensional indexing is not supported by this indexer; use 'System.Int32' or 'System.String' for array and object indexing respectively."; [Fact] public void SettingDifferentValueTypes() { dynamic dyn = new JsonObject(); dyn.boolean = AnyInstance.AnyBool; dyn.int16 = AnyInstance.AnyShort; dyn.int32 = AnyInstance.AnyInt; dyn.int64 = AnyInstance.AnyLong; dyn.uint16 = AnyInstance.AnyUShort; dyn.uint32 = AnyInstance.AnyUInt; dyn.uint64 = AnyInstance.AnyULong; dyn.@char = AnyInstance.AnyChar; dyn.dbl = AnyInstance.AnyDouble; dyn.flt = AnyInstance.AnyFloat; dyn.dec = AnyInstance.AnyDecimal; dyn.str = AnyInstance.AnyString; dyn.uri = AnyInstance.AnyUri; dyn.@byte = AnyInstance.AnyByte; dyn.@sbyte = AnyInstance.AnySByte; dyn.guid = AnyInstance.AnyGuid; dyn.dateTime = AnyInstance.AnyDateTime; dyn.dateTimeOffset = AnyInstance.AnyDateTimeOffset; dyn.JsonArray = AnyInstance.AnyJsonArray; dyn.JsonPrimitive = AnyInstance.AnyJsonPrimitive; dyn.JsonObject = AnyInstance.AnyJsonObject; JsonObject jo = (JsonObject)dyn; Assert.Equal(AnyInstance.AnyBool, (bool)jo["boolean"]); Assert.Equal(AnyInstance.AnyShort, (short)jo["int16"]); Assert.Equal(AnyInstance.AnyUShort, (ushort)jo["uint16"]); Assert.Equal(AnyInstance.AnyInt, (int)jo["int32"]); Assert.Equal(AnyInstance.AnyUInt, (uint)jo["uint32"]); Assert.Equal(AnyInstance.AnyLong, (long)jo["int64"]); Assert.Equal(AnyInstance.AnyULong, (ulong)jo["uint64"]); Assert.Equal(AnyInstance.AnySByte, (sbyte)jo["sbyte"]); Assert.Equal(AnyInstance.AnyByte, (byte)jo["byte"]); Assert.Equal(AnyInstance.AnyChar, (char)jo["char"]); Assert.Equal(AnyInstance.AnyDouble, (double)jo["dbl"]); Assert.Equal(AnyInstance.AnyFloat, (float)jo["flt"]); Assert.Equal(AnyInstance.AnyDecimal, (decimal)jo["dec"]); Assert.Equal(AnyInstance.AnyString, (string)jo["str"]); Assert.Equal(AnyInstance.AnyUri, (Uri)jo["uri"]); Assert.Equal(AnyInstance.AnyGuid, (Guid)jo["guid"]); Assert.Equal(AnyInstance.AnyDateTime, (DateTime)jo["dateTime"]); Assert.Equal(AnyInstance.AnyDateTimeOffset, (DateTimeOffset)jo["dateTimeOffset"]); Assert.Same(AnyInstance.AnyJsonArray, jo["JsonArray"]); Assert.Equal(AnyInstance.AnyJsonPrimitive, jo["JsonPrimitive"]); Assert.Same(AnyInstance.AnyJsonObject, jo["JsonObject"]); Assert.Equal(AnyInstance.AnyBool, (bool)dyn.boolean); Assert.Equal(AnyInstance.AnyShort, (short)dyn.int16); Assert.Equal(AnyInstance.AnyUShort, (ushort)dyn.uint16); Assert.Equal(AnyInstance.AnyInt, (int)dyn.int32); Assert.Equal(AnyInstance.AnyUInt, (uint)dyn.uint32); Assert.Equal(AnyInstance.AnyLong, (long)dyn.int64); Assert.Equal(AnyInstance.AnyULong, (ulong)dyn.uint64); Assert.Equal(AnyInstance.AnySByte, (sbyte)dyn.@sbyte); Assert.Equal(AnyInstance.AnyByte, (byte)dyn.@byte); Assert.Equal(AnyInstance.AnyChar, (char)dyn.@char); Assert.Equal(AnyInstance.AnyDouble, (double)dyn.dbl); Assert.Equal(AnyInstance.AnyFloat, (float)dyn.flt); Assert.Equal(AnyInstance.AnyDecimal, (decimal)dyn.dec); Assert.Equal(AnyInstance.AnyString, (string)dyn.str); Assert.Equal(AnyInstance.AnyUri, (Uri)dyn.uri); Assert.Equal(AnyInstance.AnyGuid, (Guid)dyn.guid); Assert.Equal(AnyInstance.AnyDateTime, (DateTime)dyn.dateTime); Assert.Equal(AnyInstance.AnyDateTimeOffset, (DateTimeOffset)dyn.dateTimeOffset); Assert.Same(AnyInstance.AnyJsonArray, dyn.JsonArray); Assert.Equal(AnyInstance.AnyJsonPrimitive, dyn.JsonPrimitive); Assert.Same(AnyInstance.AnyJsonObject, dyn.JsonObject); ExceptionHelper.Throws(delegate { dyn.other = Console.Out; }); ExceptionHelper.Throws(delegate { dyn.other = dyn.NonExistentProp; }); } [Fact] public void NullTests() { dynamic dyn = new JsonObject(); JsonObject jo = (JsonObject)dyn; dyn.@null = null; Assert.Same(dyn.@null, AnyInstance.DefaultJsonValue); jo["@null"] = null; Assert.Null(jo["@null"]); } [Fact] public void DynamicNotationTest() { bool boolValue; JsonValue jsonValue; Person person = Person.CreateSample(); dynamic jo = JsonValueExtensions.CreateFrom(person); dynamic target = jo; Assert.Equal(person.Age, target.Age.ReadAs()); // JsonPrimitive Assert.Equal(person.Address.ToString(), ((JsonObject)target.Address).ReadAsType
().ToString()); // JsonObject target = jo.Address.City; // JsonPrimitive Assert.NotNull(target); Assert.Equal(target.ReadAs(), person.Address.City); target = jo.Friends; // JsonArray Assert.NotNull(target); jsonValue = target as JsonValue; Assert.Equal(person.Friends.Count, jsonValue.ReadAsType>().Count); target = jo.Friends[1].Address.City; Assert.NotNull(target); Assert.Equal(target.ReadAs(), person.Address.City); target = jo.Address.NonExistentProp.NonExistentProp2; // JsonObject (default) Assert.NotNull(target); Assert.True(jo is JsonObject); Assert.False(target.TryReadAs(out boolValue)); Assert.True(target.TryReadAs(out jsonValue)); Assert.Same(target, jsonValue); Assert.Same(jo.Address.NonExistent, AnyInstance.DefaultJsonValue); Assert.Same(jo.Friends[1000], AnyInstance.DefaultJsonValue); Assert.Same(jo.Age.NonExistentProp, AnyInstance.DefaultJsonValue); Assert.Same(jo.Friends.NonExistentProp, AnyInstance.DefaultJsonValue); } [Fact] public void PropertyAccessTest() { Person p = AnyInstance.AnyPerson; JsonObject jo = JsonValueExtensions.CreateFrom(p) as JsonObject; JsonArray ja = JsonValueExtensions.CreateFrom(p.Friends) as JsonArray; JsonPrimitive jp = AnyInstance.AnyJsonPrimitive; JsonValue jv = AnyInstance.DefaultJsonValue; dynamic jod = jo; dynamic jad = ja; dynamic jpd = jp; dynamic jvd = jv; Assert.Equal(jo.Count, jod.Count); Assert.Equal(jo.JsonType, jod.JsonType); Assert.Equal(jo.Keys.Count, jod.Keys.Count); Assert.Equal(jo.Values.Count, jod.Values.Count); Assert.Equal(p.Age, (int)jod.Age); Assert.Equal(p.Age, (int)jod["Age"]); Assert.Equal(p.Age, (int)jo["Age"]); Assert.Equal(p.Address.City, (string)jo["Address"]["City"]); Assert.Equal(p.Address.City, (string)jod["Address"]["City"]); Assert.Equal(p.Address.City, (string)jod.Address.City); Assert.Equal(p.Friends.Count, ja.Count); Assert.Equal(ja.Count, jad.Count); Assert.Equal(ja.IsReadOnly, jad.IsReadOnly); Assert.Equal(ja.JsonType, jad.JsonType); Assert.Equal(p.Friends[0].Age, (int)ja[0]["Age"]); Assert.Equal(p.Friends[0].Age, (int)jad[0].Age); Assert.Equal(jp.JsonType, jpd.JsonType); } [Fact] public void ConcatDynamicAssignmentTest() { string value = "MyValue"; dynamic dynArray = JsonValue.Parse(AnyInstance.AnyJsonArray.ToString()); dynamic dynObj = JsonValue.Parse(AnyInstance.AnyJsonObject.ToString()); JsonValue target; target = dynArray[0] = dynArray[1] = dynArray[2] = value; Assert.Equal((string)target, value); Assert.Equal((string)dynArray[0], value); Assert.Equal((string)dynArray[1], value); Assert.Equal((string)dynArray[2], value); target = dynObj["key0"] = dynObj["key1"] = dynObj["key2"] = value; Assert.Equal((string)target, value); Assert.Equal((string)dynObj["key0"], value); Assert.Equal((string)dynObj["key1"], value); Assert.Equal((string)dynObj["key2"], value); foreach (KeyValuePair pair in AnyInstance.AnyJsonObject) { Assert.Equal(AnyInstance.AnyJsonObject[pair.Key].ToString(), dynObj[pair.Key].ToString()); } } [Fact] public void IndexConversionTest() { dynamic target = AnyInstance.AnyJsonArray; dynamic expected = AnyInstance.AnyJsonArray[0]; dynamic result; dynamic[] zero_indexes = { (short)0, (ushort)0, (byte)0, (sbyte)0, (char)0, (int)0 }; result = target[(short)0]; Assert.Same(expected, result); result = target[(ushort)0]; Assert.Same(expected, result); result = target[(byte)0]; Assert.Same(expected, result); result = target[(sbyte)0]; Assert.Same(expected, result); result = target[(char)0]; Assert.Same(expected, result); foreach (dynamic zero_index in zero_indexes) { result = target[zero_index]; Assert.Same(expected, result); } } [Fact] public void InvalidIndexTest() { object index1 = new object(); bool index2 = true; Person index3 = AnyInstance.AnyPerson; JsonObject jo = AnyInstance.AnyJsonObject; dynamic target; object ret; JsonValue[] values = { AnyInstance.AnyJsonObject, AnyInstance.AnyJsonArray }; foreach (JsonValue value in values) { target = value; ExceptionHelper.Throws(delegate { ret = target[index1]; }, String.Format(InvalidIndexType, index1.GetType().FullName)); ExceptionHelper.Throws(delegate { ret = target[index2]; }, String.Format(InvalidIndexType, index2.GetType().FullName)); ExceptionHelper.Throws(delegate { ret = target[index3]; }, String.Format(InvalidIndexType, index3.GetType().FullName)); ExceptionHelper.Throws(delegate { ret = target[null]; }, NonSingleNonNullIndexNotSupported); ExceptionHelper.Throws(delegate { ret = target[0, 1]; }, NonSingleNonNullIndexNotSupported); ExceptionHelper.Throws(delegate { ret = target["key1", "key2"]; }, NonSingleNonNullIndexNotSupported); ExceptionHelper.Throws(delegate { ret = target[true]; }, String.Format(InvalidIndexType, true.GetType().FullName)); ExceptionHelper.Throws(delegate { target[index1] = jo; }, String.Format(InvalidIndexType, index1.GetType().FullName)); ExceptionHelper.Throws(delegate { target[index2] = jo; }, String.Format(InvalidIndexType, index2.GetType().FullName)); ExceptionHelper.Throws(delegate { target[index3] = jo; }, String.Format(InvalidIndexType, index3.GetType().FullName)); ExceptionHelper.Throws(delegate { target[null] = jo; }, NonSingleNonNullIndexNotSupported); ExceptionHelper.Throws(delegate { target[0, 1] = jo; }, NonSingleNonNullIndexNotSupported); ExceptionHelper.Throws(delegate { target["key1", "key2"] = jo; }, NonSingleNonNullIndexNotSupported); ExceptionHelper.Throws(delegate { target[true] = jo; }, String.Format(InvalidIndexType, true.GetType().FullName)); } } [Fact] public void InvalidCastingTests() { dynamic dyn; string value = "NameValue"; dyn = AnyInstance.AnyJsonPrimitive; ExceptionHelper.Throws(delegate { dyn.name = value; }); dyn = AnyInstance.AnyJsonArray; ExceptionHelper.Throws(delegate { dyn.name = value; }); dyn = new JsonObject(AnyInstance.AnyJsonObject); dyn.name = value; Assert.Equal((string)dyn.name, value); dyn = AnyInstance.DefaultJsonValue; ExceptionHelper.Throws(delegate { dyn.name = value; }); } [Fact] public void CastTests() { dynamic dyn = JsonValueExtensions.CreateFrom(AnyInstance.AnyPerson) as JsonObject; string city = dyn.Address.City; Assert.Equal(AnyInstance.AnyPerson.Address.City, dyn.Address.City.ReadAs()); Assert.Equal(AnyInstance.AnyPerson.Address.City, city); JsonValue[] values = { AnyInstance.AnyInt, AnyInstance.AnyString, AnyInstance.AnyDateTime, AnyInstance.AnyJsonObject, AnyInstance.AnyJsonArray, AnyInstance.DefaultJsonValue }; int loopCount = 2; bool explicitCast = true; while (loopCount > 0) { loopCount--; foreach (JsonValue jv in values) { EvaluateNoExceptions(null, explicitCast); EvaluateNoExceptions(jv, explicitCast); EvaluateNoExceptions(jv, explicitCast); EvaluateNoExceptions(jv, explicitCast); EvaluateNoExceptions>>(jv, explicitCast); EvaluateNoExceptions(null, explicitCast); EvaluateExpectExceptions(null, explicitCast); EvaluateExpectExceptions(jv, explicitCast); EvaluateExpectExceptions(jv, explicitCast); EvaluateIgnoreExceptions(jv, explicitCast); EvaluateIgnoreExceptions(jv, explicitCast); EvaluateIgnoreExceptions(jv, explicitCast); EvaluateIgnoreExceptions(jv, explicitCast); EvaluateIgnoreExceptions(jv, explicitCast); EvaluateIgnoreExceptions(jv, explicitCast); } explicitCast = false; } EvaluateNoExceptions>(AnyInstance.AnyJsonObject, false); EvaluateNoExceptions>(AnyInstance.AnyJsonArray, false); } static void EvaluateNoExceptions(JsonValue value, bool cast) { Evaluate(value, cast, false, true); } static void EvaluateExpectExceptions(JsonValue value, bool cast) { Evaluate(value, cast, true, true); } static void EvaluateIgnoreExceptions(JsonValue value, bool cast) { Evaluate(value, cast, true, false); } static void Evaluate(JsonValue value, bool cast, bool throwExpected, bool assertExceptions) { T ret2; object obj = null; bool exceptionThrown = false; string retstr2, retstr1; Console.WriteLine("Test info: expected:[{0}], explicitCast type:[{1}]", value, typeof(T)); try { if (typeof(int) == typeof(T)) { obj = ((int)value); } else if (typeof(string) == typeof(T)) { obj = ((string)value); } else if (typeof(DateTime) == typeof(T)) { obj = ((DateTime)value); } else if (typeof(IList) == typeof(T)) { obj = (IList)value; } else if (typeof(IDictionary) == typeof(T)) { obj = (IDictionary)value; } else if (typeof(JsonValue) == typeof(T)) { obj = (JsonValue)value; } else if (typeof(JsonObject) == typeof(T)) { obj = (JsonObject)value; } else if (typeof(JsonArray) == typeof(T)) { obj = (JsonArray)value; } else if (typeof(JsonPrimitive) == typeof(T)) { obj = (JsonPrimitive)value; } else { obj = (T)(object)value; } retstr1 = obj == null ? "null" : obj.ToString(); } catch (Exception ex) { exceptionThrown = true; retstr1 = ex.Message; } if (assertExceptions) { Assert.Equal(throwExpected, exceptionThrown); } exceptionThrown = false; try { dynamic dyn = value as dynamic; if (cast) { ret2 = (T)dyn; } else { ret2 = dyn; } retstr2 = ret2 != null ? ret2.ToString() : "null"; } catch (Exception ex) { exceptionThrown = true; retstr2 = ex.Message; } if (assertExceptions) { Assert.Equal(throwExpected, exceptionThrown); } // fixup string retstr1 = retstr1.Replace("\'Person\'", String.Format("\'{0}\'", typeof(Person).FullName)); if (retstr1.EndsWith(".")) retstr1 = retstr1.Substring(0, retstr1.Length - 1); // fixup string retstr2 = retstr2.Replace("\'string\'", String.Format("\'{0}\'", typeof(string).FullName)); retstr2 = retstr2.Replace("\'int\'", String.Format("\'{0}\'", typeof(int).FullName)); if (retstr2.EndsWith(".")) retstr2 = retstr2.Substring(0, retstr2.Length - 1); Assert.Equal(retstr1, retstr2); } } }