Files
acceptance-tests
data
debian
docs
external
Newtonsoft.Json
api-doc-tools
api-snapshot
aspnetwebstack
packages
src
test
Microsoft.TestCommon
Microsoft.Web.Helpers.Test
Microsoft.Web.Http.Data.Test
Microsoft.Web.Mvc.Test
Microsoft.Web.WebPages.OAuth.Test
SPA.Test
System.Json.Test.Integration
System.Json.Test.Unit
System.Net.Http.Formatting.Test.Integration
Properties
FormUrlEncodedFromContentTests.cs
FormUrlEncodedFromUriQueryTests.cs
JTokenRoundTripComparer.cs
JsonNetSerializationTest.cs
JsonNetValidationTest.cs
JsonValueCreatorSurrogate.cs
System.Net.Http.Formatting.Test.Integration.csproj
packages.config
System.Net.Http.Formatting.Test.Unit
System.Web.Helpers.Test
System.Web.Http.Integration.Test
System.Web.Http.SelfHost.Test
System.Web.Http.Test
System.Web.Http.WebHost.Test
System.Web.Mvc.Test
System.Web.Razor.Test
System.Web.WebPages.Administration.Test
System.Web.WebPages.Deployment.Test
System.Web.WebPages.Razor.Test
System.Web.WebPages.Test
WebMatrix.Data.Test
WebMatrix.WebData.Test
Settings.StyleCop
tools
.gitattributes
.gitignore
License.txt
README.md
Runtime.msbuild
Runtime.sln
Runtime.xunit
Settings.StyleCop
build.cmd
bdwgc
binary-reference-assemblies
bockbuild
boringssl
cecil
cecil-legacy
corefx
corert
helix-binaries
ikdasm
ikvm
illinker-test-assets
linker
llvm-project
nuget-buildtasks
nunit-lite
roslyn-binaries
rx
xunit-binaries
how-to-bump-roslyn-binaries.md
ikvm-native
llvm
m4
man
mcs
mono
msvc
netcore
po
runtime
samples
scripts
support
tools
COPYING.LIB
LICENSE
Makefile.am
Makefile.in
NEWS
README.md
acinclude.m4
aclocal.m4
autogen.sh
code_of_conduct.md
compile
config.guess
config.h.in
config.rpath
config.sub
configure.REMOVED.git-id
configure.ac.REMOVED.git-id
depcomp
install-sh
ltmain.sh.REMOVED.git-id
missing
mkinstalldirs
mono-uninstalled.pc.in
test-driver
winconfig.h
Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

519 lines
18 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TestCommon;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Xunit;
using Xunit.Extensions;
namespace System.Net.Http.Formatting
{
public class JsonNetSerializationTest
{
public static IEnumerable<object[]> SerializedJson
{
get
{
return new TheoryDataSet<object, string>()
{
// Primitives
{ 'f', "\"f\"" },
{ "abc", "\"abc\"" },
{ "\"\\", @"""\""\\""" },
{ 256, "256" },
{ (ulong)long.MaxValue, long.MaxValue.ToString() },
{ 45.78m, "45.78" },
{ .00000457823432, "4.57823432E-06" },
{ (byte)24, "24" },
{ false, "false" },
{ AttributeTargets.Assembly | AttributeTargets.Constructor, "33" },
{ ConsoleColor.DarkCyan, "3" },
{ new DateTimeOffset(1999, 5, 27, 4, 34, 45, TimeSpan.Zero), "\"1999-05-27T04:34:45+00:00\"" },
{ new TimeSpan(5, 30, 0), "\"05:30:00\"" },
{ new Uri("http://www.bing.com"), @"""http://www.bing.com/""" },
{ new Guid("4ed1cd44-11d7-4b27-b623-0b8b553c8906"), "\"4ed1cd44-11d7-4b27-b623-0b8b553c8906\"" },
// Structs
{ new Point() { x = 45, Y = -5}, "{\"x\":45,\"Y\":-5}" },
// Arrays
{ new object[] {}, "[]" },
{ new int[] { 1, 2, 3}, "[1,2,3]" },
{ new string[] { "a", "b"}, "[\"a\",\"b\"]" },
{ new Point[] { new Point() { x = 10, Y = 10}, new Point() { x = 20, Y = 20}}, "[{\"x\":10,\"Y\":10},{\"x\":20,\"Y\":20}]" },
// Collections
{ new List<int> { 1, 2, 3}, "[1,2,3]" },
{ new List<string> { "a", "b"}, "[\"a\",\"b\"]" },
{ new List<Point> { new Point() { x = 10, Y = 10}, new Point() { x = 20, Y = 20}}, "[{\"x\":10,\"Y\":10},{\"x\":20,\"Y\":20}]" },
{ new MyList<int> { 1, 2, 3}, "[1,2,3]" },
{ new MyList<string> { "a", "b"}, "[\"a\",\"b\"]" },
{ new MyList<Point> { new Point() { x = 10, Y = 10}, new Point() { x = 20, Y = 20}}, "[{\"x\":10,\"Y\":10},{\"x\":20,\"Y\":20}]" },
// Dictionaries
{ new Dictionary<string, string> { { "k1", "v1" }, { "k2", "v2" } }, "{\"k1\":\"v1\",\"k2\":\"v2\"}" },
{ new Dictionary<int, string> { { 1, "v1" }, { 2, "v2" } }, "{\"1\":\"v1\",\"2\":\"v2\"}" },
// Anonymous types
{ new { Anon1 = 56, Anon2 = "foo"}, "{\"Anon1\":56,\"Anon2\":\"foo\"}" },
// Classes
{ new DataContractType() { s = "foo", i = 49, NotAMember = "Error" }, "{\"s\":\"foo\",\"i\":49}" },
{ new POCOType() { s = "foo", t = "Error"}, "{\"s\":\"foo\"}" },
{ new SerializableType("protected") { publicField = "public", protectedInternalField = "protected internal", internalField = "internal", PublicProperty = "private", nonSerializedField = "Error" }, "{\"publicField\":\"public\",\"internalField\":\"internal\",\"protectedInternalField\":\"protected internal\",\"protectedField\":\"protected\",\"privateField\":\"private\"}" },
// Generics
{ new KeyValuePair<string, bool>("foo", false), "{\"Key\":\"foo\",\"Value\":false}" },
// ISerializable types
{ new ArgumentNullException("param"), "{\"ClassName\":\"System.ArgumentNullException\",\"Message\":\"Value cannot be null.\",\"Data\":null,\"InnerException\":null,\"HelpURL\":null,\"StackTraceString\":null,\"RemoteStackTraceString\":null,\"RemoteStackIndex\":0,\"ExceptionMethod\":null,\"HResult\":-2147467261,\"Source\":null,\"WatsonBuckets\":null,\"ParamName\":\"param\"}" },
// JSON Values
{ new JValue(false), "false" },
{ new JValue(54), "54" },
{ new JValue("s"), "\"s\"" },
{ new JArray() { new JValue(1), new JValue(2) }, "[1,2]" },
{ new JObject() { { "k1", new JValue("v1") }, { "k2", new JValue("v2") } }, "{\"k1\":\"v1\",\"k2\":\"v2\"}" },
{ new KeyValuePair<JToken, JToken>(new JValue("k"), new JArray() { new JValue("v1"), new JValue("v2") }), "{\"Key\":\"k\",\"Value\":[\"v1\",\"v2\"]}" },
};
}
}
public static IEnumerable<object[]> TypedSerializedJson
{
get
{
return new TheoryDataSet<object, string, Type>()
{
// Null
{ null, "null", typeof(POCOType) },
{ null, "null", typeof(JToken) },
// Nullables
{ new int?(), "null", typeof(int?) },
{ new Point?(), "null", typeof(Point?) },
{ new ConsoleColor?(), "null", typeof(ConsoleColor?) },
{ new int?(45), "45", typeof(int?) },
{ new Point?(new Point() { x = 45, Y = -5 }), "{\"x\":45,\"Y\":-5}", typeof(Point?) },
{ new ConsoleColor?(ConsoleColor.DarkMagenta), "5", typeof(ConsoleColor?)},
};
}
}
[Theory]
[PropertyData("SerializedJson")]
public void ObjectsSerializeToExpectedJson(object o, string expectedJson)
{
ObjectsSerializeToExpectedJsonWithProvidedType(o, expectedJson, o.GetType());
}
[Theory]
[PropertyData("SerializedJson")]
public void JsonDeserializesToExpectedObject(object expectedObject, string json)
{
JsonDeserializesToExpectedObjectWithProvidedType(expectedObject, json, expectedObject.GetType());
}
[Theory]
[PropertyData("TypedSerializedJson")]
public void ObjectsSerializeToExpectedJsonWithProvidedType(object o, string expectedJson, Type type)
{
Assert.Equal(expectedJson, Serialize(o, type));
}
[Theory]
[PropertyData("TypedSerializedJson")]
public void JsonDeserializesToExpectedObjectWithProvidedType(object expectedObject, string json, Type type)
{
if (expectedObject == null)
{
Assert.Null(Deserialize(json, type));
}
else
{
Assert.Equal(expectedObject, Deserialize(json, type), new ObjectComparer());
}
}
[Fact]
public void CallbacksGetCalled()
{
TypeWithCallbacks o = new TypeWithCallbacks();
string json = Serialize(o, typeof(TypeWithCallbacks));
Assert.Equal("12", o.callbackOrder);
TypeWithCallbacks deserializedObject = Deserialize(json, typeof(TypeWithCallbacks)) as TypeWithCallbacks;
Assert.Equal("34", deserializedObject.callbackOrder);
}
[Fact]
public void DerivedTypesArePreserved()
{
JsonMediaTypeFormatter formatter = new JsonMediaTypeFormatter();
formatter.SerializerSettings.TypeNameHandling = TypeNameHandling.Objects;
string json = Serialize(new Derived(), typeof(Base), formatter);
object deserializedObject = Deserialize(json, typeof(Base), formatter);
Assert.IsType(typeof(Derived), deserializedObject);
}
[Fact]
public void ArbitraryTypesArentDeserializedByDefault()
{
string json = "{\"$type\":\"" + typeof(DangerousType).AssemblyQualifiedName + "\"}";
object deserializedObject = Deserialize(json, typeof(object));
Assert.IsNotType(typeof(DangerousType), deserializedObject);
}
[Fact]
public void ReferencesArePreserved()
{
Ref ref1 = new Ref();
Ref ref2 = new Ref();
ref1.Reference = ref2;
ref2.Reference = ref1;
string json = Serialize(ref1, typeof(Ref));
Ref deserializedObject = Deserialize(json, typeof(Ref)) as Ref;
Assert.ReferenceEquals(deserializedObject, deserializedObject.Reference.Reference);
}
[Fact]
public void DeserializingDeepArraysThrows()
{
StringBuilder sb = new StringBuilder();
int depth = 500;
for (int i = 0; i < depth; i++)
{
sb.Append("[");
}
sb.Append("null");
for (int i = 0; i < depth; i++)
{
sb.Append("]");
}
string json = sb.ToString();
Assert.Throws(typeof(JsonReaderQuotaException), () => Deserialize(json, typeof(object)));
}
[Theory]
// existing good surrogate pair
[InlineData("ABC \\ud800\\udc00 DEF", "ABC \ud800\udc00 DEF")]
// invalid surrogates (two high back-to-back)
[InlineData("ABC \\ud800\\ud800 DEF", "ABC \ufffd\ufffd DEF")]
// invalid high surrogate at end of string
[InlineData("ABC \\ud800", "ABC \ufffd")]
// high surrogate not followed by low surrogate
[InlineData("ABC \\ud800 DEF", "ABC \ufffd DEF")]
// low surrogate not preceded by high surrogate
[InlineData("ABC \\udc00\\ud800 DEF", "ABC \ufffd\ufffd DEF")]
// make sure unencoded invalid surrogate characters don't make it through
[InlineData("\udc00\ud800\ud800", "??????")]
public void InvalidUnicodeStringsAreFixedUp(string input, string expectedString)
{
string json = "\"" + input + "\"";
string deserializedString = Deserialize(json, typeof(string)) as string;
Assert.Equal(expectedString, deserializedString);
}
private static string Serialize(object o, Type type, MediaTypeFormatter formatter = null)
{
formatter = formatter ?? new JsonMediaTypeFormatter();
MemoryStream ms = new MemoryStream();
formatter.WriteToStreamAsync(type, o, ms, null, null).Wait();
ms.Flush();
ms.Position = 0;
return new StreamReader(ms).ReadToEnd();
}
internal static object Deserialize(string json, Type type, MediaTypeFormatter formatter = null, IFormatterLogger formatterLogger = null)
{
formatter = formatter ?? new JsonMediaTypeFormatter();
MemoryStream ms = new MemoryStream();
byte[] bytes = Encoding.Default.GetBytes(json);
ms.Write(bytes, 0, bytes.Length);
ms.Flush();
ms.Position = 0;
Task<object> readTask = formatter.ReadFromStreamAsync(type, ms, contentHeaders: null, formatterLogger: formatterLogger);
readTask.WaitUntilCompleted();
if (readTask.IsFaulted)
{
throw readTask.Exception.GetBaseException();
}
return readTask.Result;
}
}
public class ObjectComparer : IEqualityComparer<object>
{
bool IEqualityComparer<object>.Equals(object x, object y)
{
Type xType = x.GetType();
if (xType == y.GetType())
{
if (typeof(JToken).IsAssignableFrom(xType) || xType == typeof(ArgumentNullException) || xType == typeof(KeyValuePair<JToken, JToken>))
{
return x.ToString() == y.ToString();
}
if (xType == typeof(DataContractType))
{
return Equals<DataContractType>(x, y);
}
if (xType == typeof(POCOType))
{
return Equals<POCOType>(x, y);
}
if (xType == typeof(SerializableType))
{
return Equals<SerializableType>(x, y);
}
if (xType == typeof(Point))
{
return Equals<Point>(x, y);
}
if (typeof(IEnumerable).IsAssignableFrom(xType))
{
IEnumerator xEnumerator = ((IEnumerable)x).GetEnumerator();
IEnumerator yEnumerator = ((IEnumerable)y).GetEnumerator();
while (xEnumerator.MoveNext())
{
// if x is longer than y
if (!yEnumerator.MoveNext())
{
return false;
}
else
{
if (!xEnumerator.Current.Equals(yEnumerator.Current))
{
return false;
}
}
}
// if y is longer than x
if (yEnumerator.MoveNext())
{
return false;
}
return true;
}
}
return x.Equals(y);
}
int IEqualityComparer<object>.GetHashCode(object obj)
{
throw new NotImplementedException();
}
bool Equals<T>(object x, object y) where T : IEquatable<T>
{
IEquatable<T> yEquatable = (IEquatable<T>)y;
return yEquatable.Equals((T)x);
}
}
// Marked as [Serializable] to check that [DataContract] takes precedence over [Serializable]
[DataContract]
[Serializable]
public class DataContractType : IEquatable<DataContractType>
{
[DataMember]
public string s;
[DataMember]
internal int i;
public string NotAMember;
public bool Equals(DataContractType other)
{
return this.s == other.s && this.i == other.i;
}
}
public class POCOType : IEquatable<POCOType>
{
public string s;
internal string t;
public bool Equals(POCOType other)
{
return this.s == other.s;
}
}
public class MyList<T> : ICollection<T>
{
List<T> innerList = new List<T>();
public IEnumerator<T> GetEnumerator()
{
return innerList.GetEnumerator();
}
public void Add(T item)
{
innerList.Add(item);
}
public void Clear()
{
innerList.Clear();
}
public bool Contains(T item)
{
return innerList.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
innerList.CopyTo(array, arrayIndex);
}
public int Count
{
get { return innerList.Count; }
}
public bool IsReadOnly
{
get { return ((ICollection<T>)innerList).IsReadOnly; }
}
public bool Remove(T item)
{
return innerList.Remove(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return innerList.GetEnumerator();
}
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return innerList.GetEnumerator();
}
}
[Serializable]
class SerializableType : IEquatable<SerializableType>
{
public SerializableType(string protectedFieldValue)
{
this.protectedField = protectedFieldValue;
}
public string publicField;
internal string internalField;
protected internal string protectedInternalField;
protected string protectedField;
private string privateField;
public string PublicProperty
{
get
{
return privateField;
}
set
{
this.privateField = value;
}
}
[NonSerialized]
public string nonSerializedField;
public bool Equals(SerializableType other)
{
return this.publicField == other.publicField &&
this.internalField == other.internalField &&
this.protectedInternalField == other.protectedInternalField &&
this.protectedField == other.protectedField &&
this.privateField == other.privateField;
}
}
public struct Point : IEquatable<Point>
{
public int x;
public int Y { get; set; }
public bool Equals(Point other)
{
return this.x == other.x && this.Y == other.Y;
}
}
[DataContract(IsReference = true)]
public class Ref
{
[DataMember]
public Ref Reference;
}
public class Base
{
}
public class Derived : Base
{
}
[DataContract]
public class TypeWithCallbacks
{
public string callbackOrder = "";
[OnSerializing]
public void OnSerializing(StreamingContext c)
{
callbackOrder += "1";
}
[OnSerialized]
public void OnSerialized(StreamingContext c)
{
callbackOrder += "2";
}
[OnDeserializing]
public void OnDeserializing(StreamingContext c)
{
callbackOrder += "3";
}
[OnDeserialized]
public void OnDeserialized(StreamingContext c)
{
callbackOrder += "4";
}
}
public class DangerousType
{
}
}