public readonly partial struct JsonElement
{
private readonly object _dummy;
+ private readonly int _dummyPrimitive;
public System.Text.Json.JsonElement this[int index] { get { throw null; } }
public System.Text.Json.JsonValueType Type { get { throw null; } }
public System.Text.Json.JsonElement.ArrayEnumerator EnumerateArray() { throw null; }
public partial struct ArrayEnumerator : System.Collections.Generic.IEnumerable<System.Text.Json.JsonElement>, System.Collections.Generic.IEnumerator<System.Text.Json.JsonElement>, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
{
private object _dummy;
+ private int _dummyPrimitive;
public System.Text.Json.JsonElement Current { get { throw null; } }
object System.Collections.IEnumerator.Current { get { throw null; } }
public void Dispose() { }
public partial struct ObjectEnumerator : System.Collections.Generic.IEnumerable<System.Text.Json.JsonProperty>, System.Collections.Generic.IEnumerator<System.Text.Json.JsonProperty>, System.Collections.IEnumerable, System.Collections.IEnumerator, System.IDisposable
{
private object _dummy;
+ private int _dummyPrimitive;
public System.Text.Json.JsonProperty Current { get { throw null; } }
object System.Collections.IEnumerator.Current { get { throw null; } }
public void Dispose() { }
}
public partial struct JsonReaderOptions
{
- private object _dummy;
+ private int _dummyPrimitive;
public System.Text.Json.JsonCommentHandling CommentHandling { get { throw null; } set { } }
public int MaxDepth { get { throw null; } set { } }
}
}
public partial struct JsonWriterOptions
{
- private object _dummy;
+ private int _dummyPrimitive;
public bool Indented { get { throw null; } set { } }
public bool SkipValidation { get { throw null; } set { } }
}
public partial struct JsonWriterState
{
private object _dummy;
+ private int _dummyPrimitive;
public JsonWriterState(System.Text.Json.JsonWriterOptions options = default(System.Text.Json.JsonWriterOptions)) { throw null; }
public long BytesCommitted { get { throw null; } }
public long BytesWritten { get { throw null; } }
public ref partial struct Utf8JsonReader
{
private object _dummy;
+ private int _dummyPrimitive;
public Utf8JsonReader(in System.Buffers.ReadOnlySequence<byte> jsonData, bool isFinalBlock, System.Text.Json.JsonReaderState state) { throw null; }
public Utf8JsonReader(System.ReadOnlySpan<byte> jsonData, bool isFinalBlock, System.Text.Json.JsonReaderState state) { throw null; }
public long BytesConsumed { get { throw null; } }
public ref partial struct Utf8JsonWriter
{
private object _dummy;
+ private int _dummyPrimitive;
public Utf8JsonWriter(System.Buffers.IBufferWriter<byte> bufferWriter, System.Text.Json.JsonWriterState state = default(System.Text.Json.JsonWriterState)) { throw null; }
public long BytesCommitted { get { throw null; } }
public long BytesWritten { get { throw null; } }
public void WriteStringValue(string value, bool escape = true) { }
}
}
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ public static object Parse(System.ReadOnlySpan<byte> utf8Json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static object Parse(string json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static TValue Parse<TValue>(System.ReadOnlySpan<byte> utf8Json, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static TValue Parse<TValue>(string json, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static System.Threading.Tasks.ValueTask<object> ReadAsync(System.IO.Stream utf8Json, System.Type returnType, System.Text.Json.Serialization.JsonSerializerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public static System.Threading.Tasks.ValueTask<TValue> ReadAsync<TValue>(System.IO.Stream utf8Json, System.Text.Json.Serialization.JsonSerializerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public static byte[] ToBytes(object value, System.Type type, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static byte[] ToBytes<TValue>(TValue value, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static string ToString(object value, System.Type type, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static string ToString<TValue>(TValue value, System.Text.Json.Serialization.JsonSerializerOptions options = null) { throw null; }
+ public static System.Threading.Tasks.Task WriteAsync(object value, System.Type type, System.IO.Stream utf8Json, System.Text.Json.Serialization.JsonSerializerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ public static System.Threading.Tasks.Task WriteAsync<TValue>(TValue value, System.IO.Stream utf8Json, System.Text.Json.Serialization.JsonSerializerOptions options = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
+ }
+ public sealed partial class JsonSerializerOptions
+ {
+ public JsonSerializerOptions() { }
+ public int DefaultBufferSize { get { throw null; } set { } }
+ public bool IgnoreNullPropertyValueOnRead { get { throw null; } set { } }
+ public bool IgnoreNullPropertyValueOnWrite { get { throw null; } set { } }
+ public System.Text.Json.JsonReaderOptions ReaderOptions { get { throw null; } set { } }
+ public System.Text.Json.JsonWriterOptions WriterOptions { get { throw null; } set { } }
+ }
+}
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard'">
<Reference Include="System.Memory" />
+ <Reference Include="System.Threading.Tasks.Extensions" />
</ItemGroup>
</Project>
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace System.Text.Json.Serialization
+{
+ internal class JsonReflectionEmitMaterializer : JsonMemberBasedClassMaterializer
+ {
+ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type)
+ {
+ ConstructorInfo realMethod = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic| BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null);
+ if (realMethod == null)
+ return null;
+
+ var dynamicMethod = new DynamicMethod(
+ realMethod.Name,
+ type,
+ Type.EmptyTypes,
+ typeof(JsonReflectionEmitMaterializer).Module,
+ skipVisibility: true);
+
+ if (dynamicMethod != null)
+ {
+ ILGenerator generator = dynamicMethod?.GetILGenerator();
+ if (generator != null)
+ {
+
+ generator.Emit(OpCodes.Newobj, realMethod);
+ generator.Emit(OpCodes.Ret);
+
+ var result = (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate));
+ return result;
+ }
+ }
+
+ throw new InvalidOperationException(SR.Format(SR.SerializationUnableToCreateDynamicMethod, $"{type.FullName}.{realMethod.Name}"));
+ }
+
+ public override JsonPropertyInfo<TValue>.GetterDelegate CreateGetter<TValue>(PropertyInfo propertyInfo)
+ {
+ MethodInfo realMethod = propertyInfo.GetGetMethod();
+ Debug.Assert(realMethod != null);
+
+ var dynamicMethod = new DynamicMethod(
+ realMethod.Name,
+ typeof(TValue),
+ new Type[] { typeof(object) },
+ typeof(JsonReflectionEmitMaterializer).Module,
+ skipVisibility: true);
+
+ if (dynamicMethod != null)
+ {
+
+ ILGenerator generator = dynamicMethod?.GetILGenerator();
+ if (generator != null)
+ {
+ generator.Emit(OpCodes.Ldarg_0);
+ generator.EmitCall(OpCodes.Callvirt, realMethod, null);
+ generator.Emit(OpCodes.Ret);
+
+ var result = (JsonPropertyInfo<TValue>.GetterDelegate)dynamicMethod.CreateDelegate(typeof(JsonPropertyInfo<TValue>.GetterDelegate));
+ return result;
+ }
+ }
+
+ throw new InvalidOperationException(SR.Format(SR.SerializationUnableToCreateDynamicMethod, $"{propertyInfo.Name}.{realMethod.Name}"));
+ }
+
+ public override JsonPropertyInfo<TValue>.SetterDelegate CreateSetter<TValue>(PropertyInfo propertyInfo)
+ {
+ MethodInfo realMethod = propertyInfo.GetSetMethod();
+ Debug.Assert(realMethod != null);
+
+ var dynamicMethod = new DynamicMethod(
+ realMethod.Name,
+ typeof(void),
+ new Type[] { typeof(object), typeof(TValue) },
+ typeof(JsonReflectionEmitMaterializer).Module,
+ skipVisibility: true);
+
+ if (dynamicMethod != null)
+ {
+ ILGenerator generator = dynamicMethod?.GetILGenerator();
+ if (generator != null)
+ {
+
+ generator.Emit(OpCodes.Ldarg_0);
+ generator.Emit(OpCodes.Ldarg_1);
+ generator.EmitCall(OpCodes.Callvirt, realMethod, null);
+ generator.Emit(OpCodes.Ret);
+
+ var result = (JsonPropertyInfo<TValue>.SetterDelegate)dynamicMethod.CreateDelegate(typeof(JsonPropertyInfo<TValue>.SetterDelegate));
+ return result;
+ }
+ }
+
+ throw new InvalidOperationException(SR.Format(SR.SerializationUnableToCreateDynamicMethod, $"{propertyInfo.Name}.{realMethod.Name}"));
+ }
+ }
+}
<data name="ZeroDepthAtEnd" xml:space="preserve">
<value>Expected CurrentDepth ({0}) to be zero at the end of the JSON payload. There is an open JSON object or array that should be closed.</value>
</data>
-</root>
+ <data name="DeserializeCannotBeNull" xml:space="preserve">
+ <value>The JSON value from {0} cannot be null.</value>
+ </data>
+ <data name="DeserializeDataRemaining" xml:space="preserve">
+ <value>The provided data of length {0} has remaining bytes {1}.</value>
+ </data>
+ <data name="DeserializeUnableToConvertValue" xml:space="preserve">
+ <value>The JSON value from {0} could not be converted to {1}.</value>
+ </data>
+ <data name="DeserializeWrongType" xml:space="preserve">
+ <value>The specified type {0} must derive from the specific value's type {1}.</value>
+ </data>
+ <data name="SerializationInvalidBufferSize" xml:space="preserve">
+ <value>The value must be greater than zero or equal to -1.</value>
+ </data>
+ <data name="BufferWriterAdvancedTooFar" xml:space="preserve">
+ <value>Cannot advance past the end of the buffer, which has a size of {0}.</value>
+ </data>
+ <data name="EnumConverterNotImplemented" xml:space="preserve">
+ <value>EnumConverter is not yet supported on .NET Standard 2.0.</value>
+ </data>
+</root>
\ No newline at end of file
<Compile Include="System\Text\Json\JsonHelpers.cs" />
<Compile Include="System\Text\Json\JsonTokenType.cs" />
<Compile Include="System\Text\Json\ThrowHelper.cs" />
+ <Compile Include="System\Text\Json\ThrowHelper.Serialization.cs" />
<Compile Include="System\Text\Json\Document\JsonDocument.cs" />
<Compile Include="System\Text\Json\Document\JsonDocument.DbRow.cs" />
<Compile Include="System\Text\Json\Document\JsonDocument.MetadataDb.cs" />
<Compile Include="System\Text\Json\Reader\Utf8JsonReader.cs" />
<Compile Include="System\Text\Json\Reader\Utf8JsonReader.MultiSegment.cs" />
<Compile Include="System\Text\Json\Reader\Utf8JsonReader.TryGet.cs" />
+ <Compile Include="System\Text\Json\Serialization\ArrayBufferWriter.cs" />
+ <Compile Include="System\Text\Json\Serialization\ClassMaterializer.cs" />
+ <Compile Include="System\Text\Json\Serialization\ClassType.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultArrayConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultConverters.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\DefaultEnumConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterBoolean.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterBooleanNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByte.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterByteNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterChar.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterCharNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDateTime.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDateTimeNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDecimal.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDecimalNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDouble.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterDoubleNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInfoInt16.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt16Nullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt32.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt32Nullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt64.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterInt64Nullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterSByte.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterSByteNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterSingle.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterSingleNullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterString.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterUInt16.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterUInt16Nullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterUInt32.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterUInt32Nullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterUInt64.cs" />
+ <Compile Include="System\Text\Json\Serialization\Converters\JsonValueConverterUInt64Nullable.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonClassInfo.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonClassInfo.AddProperty.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonEnumerableConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonPropertyInfo.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonPropertyInfoOfTClassTProperty.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonPropertyInfoOfTProperty.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleArray.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Stream.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleValue.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleObject.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.String.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Helpers.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.HandleNull.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Read.Span.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.ByteArray.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.Stream.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleEnumerable.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleObject.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.String.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializer.Write.HandleValue.cs" />
+ <Compile Include="System\Text\Json\Serialization\JsonSerializerOptions.cs" />
+ <Compile Include="System\Text\Json\Serialization\Policies\JsonValueConverter.cs" />
+ <Compile Include="System\Text\Json\Serialization\PropertyRef.cs" />
+ <Compile Include="System\Text\Json\Serialization\ReadStack.cs" />
+ <Compile Include="System\Text\Json\Serialization\ReadStackFrame.cs" />
+ <Compile Include="System\Text\Json\Serialization\ReflectionEmitMaterializer.cs" />
+ <Compile Include="System\Text\Json\Serialization\ReflectionMaterializer.cs" />
+ <Compile Include="System\Text\Json\Serialization\WriteStack.cs" />
+ <Compile Include="System\Text\Json\Serialization\WriteStackFrame.cs" />
<Compile Include="System\Text\Json\Writer\JsonWriterHelper.cs" />
<Compile Include="System\Text\Json\Writer\JsonWriterHelper.Escaping.cs" />
<Compile Include="System\Text\Json\Writer\JsonWriterHelper.Transcoding.cs" />
<Compile Include="System\Text\Json\Writer\Utf8JsonWriter.WriteValues.UnsignedNumber.cs" />
</ItemGroup>
<ItemGroup Condition="'$(TargetsNETStandard)' != 'true'">
+ <Reference Include="System.Collections" />
<Reference Include="System.Diagnostics.Debug" />
+ <Reference Include="System.Reflection.Primitives" />
+ <Reference Include="System.Reflection.Emit.ILGeneration" />
+ <Reference Include="System.Reflection.Emit.Lightweight" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Text.Encoding.Extensions" />
- <Reference Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Buffers" />
+ <Reference Include="System.Collections.Concurrent" />
+ <Reference Include="System.Linq" />
<Reference Include="System.Memory" />
<Reference Include="System.Numerics.Vectors" />
<Reference Include="System.Runtime.CompilerServices.Unsafe" />
+ <Reference Include="System.Threading.Tasks" />
+ <Reference Include="System.Threading.Tasks.Extensions" />
</ItemGroup>
<!-- The Source Package will call into this target in order to retrieve the compiled resources file as
it will need to be part of the source package contents. -->
<Target Name="GetSourcesToPackage" Returns="@(_sourceToPackage)" DependsOnTargets="Compile">
-
<PropertyGroup>
<_sourcePackageName>Microsoft.Bcl.Json.Sources</_sourcePackageName>
<_sourcePackageTargetLines><![CDATA[<?xml version="1.0" encoding="utf-8"?>
<ItemGroup>
<_sourceToExclude Include="@(Compile)" Condition="$([System.String]::Copy('%(Compile.Identity)').ToLower().EndsWith('assemblyinfo.cs'))" />
- <_sourceToPackage Include="@(Compile->'%(FullPath)')" PackagePath="contentFiles/cs/netstandard2.0/$(_sourcePackageName)/%(FileName)%(Extension)" Exclude="@(_sourceToExclude)"/>
- <_sourceToPackage Condition="'%(Extension)' == '.resx'" Include="@(EmbeddedResource->'%(FullPath)')" PackagePath="build/netstandard2.0/%(FileName)%(Extension)" />
+ <_sourceToPackage Include="@(Compile->'%(FullPath)')" PackagePath="contentFiles/cs/netstandard2.0/$(_sourcePackageName)/%(FileName)%(Extension)" Exclude="@(_sourceToExclude)" />
+ <_sourceToPackage Condition="'%(Extension)' == '.resx'" Include="@(EmbeddedResource->'%(FullPath)')" PackagePath="build/netstandard2.0/%(FileName)%(Extension)" />
<_sourceToPackage Include="$(_generatedSourcePackageTargetPath)" PackagePath="build/netstandard2.0/$(_targetsFileName)" />
<_sourceToPackage Include="$(MSBuildThisFileDirectory)../source_package/README.md" PackagePath="build/netstandard2.0/README.md" />
</ItemGroup>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsInRangeInclusive(int value, int lowerBound, int upperBound)
=> (uint)(value - lowerBound) <= (uint)(upperBound - lowerBound);
+
+ /// <summary>
+ /// Returns <see langword="true"/> iff <paramref name="value"/> is between
+ /// <paramref name="lowerBound"/> and <paramref name="upperBound"/>, inclusive.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRangeInclusive(double value, double lowerBound, double upperBound)
+ // For floating-point, do a direct comparison as it is more accurate than subtracting.
+ => (value >= lowerBound) && (value <= upperBound);
+
+ /// <summary>
+ /// Returns <see langword="true"/> iff <paramref name="value"/> is between
+ /// <paramref name="lowerBound"/> and <paramref name="upperBound"/>, inclusive.
+ /// </summary>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool IsInRangeInclusive(JsonTokenType value, JsonTokenType lowerBound, JsonTokenType upperBound)
+ => (value - lowerBound) <= (upperBound - lowerBound);
}
}
BytePositionInLine = bytePositionInLine;
}
+ internal JsonReaderException(string message, in JsonReaderState state) : base(message)
+ {
+ LineNumber = state._lineNumber;
+ BytePositionInLine = state._bytePositionInLine;
+ }
+
private JsonReaderException(SerializationInfo info, StreamingContext context) : base(info, context)
{
LineNumber = info.GetInt64("LineNumber");
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ // Note: this is currently an internal class that will be replaced with a shared version.
+ internal sealed class ArrayBufferWriter<T> : IBufferWriter<T>, IDisposable
+ {
+ private T[] _rentedBuffer;
+ private int _index;
+
+ private const int MinimumBufferSize = 256;
+
+ public ArrayBufferWriter()
+ {
+ _rentedBuffer = ArrayPool<T>.Shared.Rent(MinimumBufferSize);
+ _index = 0;
+ }
+
+ public ArrayBufferWriter(int initialCapacity)
+ {
+ if (initialCapacity <= 0)
+ throw new ArgumentException(nameof(initialCapacity));
+
+ _rentedBuffer = ArrayPool<T>.Shared.Rent(initialCapacity);
+ _index = 0;
+ }
+
+ public ReadOnlyMemory<T> WrittenMemory
+ {
+ get
+ {
+ CheckIfDisposed();
+
+ return _rentedBuffer.AsMemory(0, _index);
+ }
+ }
+
+ public int WrittenCount
+ {
+ get
+ {
+ CheckIfDisposed();
+
+ return _index;
+ }
+ }
+
+ public int Capacity
+ {
+ get
+ {
+ CheckIfDisposed();
+
+ return _rentedBuffer.Length;
+ }
+ }
+
+ public int FreeCapacity
+ {
+ get
+ {
+ CheckIfDisposed();
+
+ return _rentedBuffer.Length - _index;
+ }
+ }
+
+ public void Clear()
+ {
+ CheckIfDisposed();
+
+ ClearHelper();
+ }
+
+ private void ClearHelper()
+ {
+ Debug.Assert(_rentedBuffer != null);
+
+ _rentedBuffer.AsSpan(0, _index).Clear();
+ _index = 0;
+ }
+
+ // Returns the rented buffer back to the pool
+ public void Dispose()
+ {
+ if (_rentedBuffer == null)
+ {
+ return;
+ }
+
+ ClearHelper();
+ ArrayPool<T>.Shared.Return(_rentedBuffer);
+ _rentedBuffer = null;
+ }
+
+ private void CheckIfDisposed()
+ {
+ if (_rentedBuffer == null)
+ ThrowHelper.ThrowObjectDisposedException(nameof(ArrayBufferWriter<T>));
+ }
+
+ public void Advance(int count)
+ {
+ CheckIfDisposed();
+
+ if (count < 0)
+ throw new ArgumentException(nameof(count));
+
+ if (_index > _rentedBuffer.Length - count)
+ ThrowInvalidOperationException(_rentedBuffer.Length);
+
+ _index += count;
+ }
+
+ public Memory<T> GetMemory(int sizeHint = 0)
+ {
+ CheckIfDisposed();
+
+ CheckAndResizeBuffer(sizeHint);
+ return _rentedBuffer.AsMemory(_index);
+ }
+
+ public Span<T> GetSpan(int sizeHint = 0)
+ {
+ CheckIfDisposed();
+
+ CheckAndResizeBuffer(sizeHint);
+ return _rentedBuffer.AsSpan(_index);
+ }
+
+ private void CheckAndResizeBuffer(int sizeHint)
+ {
+ Debug.Assert(_rentedBuffer != null);
+
+ if (sizeHint < 0)
+ throw new ArgumentException(nameof(sizeHint));
+
+ if (sizeHint == 0)
+ {
+ sizeHint = MinimumBufferSize;
+ }
+
+ int availableSpace = _rentedBuffer.Length - _index;
+
+ if (sizeHint > availableSpace)
+ {
+ int growBy = Math.Max(sizeHint, _rentedBuffer.Length);
+
+ int newSize = checked(_rentedBuffer.Length + growBy);
+
+ T[] oldBuffer = _rentedBuffer;
+
+ _rentedBuffer = ArrayPool<T>.Shared.Rent(newSize);
+
+ Debug.Assert(oldBuffer.Length >= _index);
+ Debug.Assert(_rentedBuffer.Length >= _index);
+
+ Span<T> previousBuffer = oldBuffer.AsSpan(0, _index);
+ previousBuffer.CopyTo(_rentedBuffer);
+ previousBuffer.Clear();
+ ArrayPool<T>.Shared.Return(oldBuffer);
+ }
+
+ Debug.Assert(_rentedBuffer.Length - _index > 0);
+ Debug.Assert(_rentedBuffer.Length - _index >= sizeHint);
+ }
+
+ private static void ThrowInvalidOperationException(int capacity)
+ {
+ throw new InvalidOperationException(SR.Format(SR.BufferWriterAdvancedTooFar, capacity));
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ internal abstract class ClassMaterializer
+ {
+ public abstract JsonClassInfo.ConstructorDelegate CreateConstructor(Type classType);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ /// <summary>
+ /// Determines how a given class is treated when it is (de)serialized.
+ /// </summary>
+ internal enum ClassType
+ {
+ Object = 0,
+ Value = 1,
+ Enumerable = 2,
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class DefaultArrayConverter : JsonEnumerableConverter
+ {
+ public override IEnumerable CreateFromList(Type elementType, IList sourceList)
+ {
+ Array array;
+
+ if (sourceList.Count > 0 && sourceList[0] is Array probe)
+ {
+ array = Array.CreateInstance(probe.GetType(), sourceList.Count);
+
+ int i = 0;
+ foreach (IList child in sourceList)
+ {
+ if (child is Array childArray)
+ {
+ array.SetValue(childArray, i++);
+ }
+ }
+ }
+ else
+ {
+ array = Array.CreateInstance(elementType, sourceList.Count);
+ sourceList.CopyTo(array, 0);
+ }
+
+ return array;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal static class DefaultConverters
+ {
+ private const int MaxTypeCode = 18;
+
+ private static readonly object[] s_Converters = new object[MaxTypeCode + 1] {
+ null, // Empty
+ null, // Object
+ null, // DBNull
+ new JsonValueConverterBoolean(),
+ new JsonValueConverterChar(),
+ new JsonValueConverterSByte(),
+ new JsonValueConverterByte(),
+ new JsonValueConverterInt16(),
+ new JsonValueConverterUInt16(),
+ new JsonValueConverterInt32(),
+ new JsonValueConverterUInt32(),
+ new JsonValueConverterInt64(),
+ new JsonValueConverterUInt64(),
+ new JsonValueConverterSingle(),
+ new JsonValueConverterDouble(),
+ new JsonValueConverterDecimal(),
+ new JsonValueConverterDateTime(),
+ null, // (not a value)
+ new JsonValueConverterString()
+ };
+
+ private static readonly object[] s_NullableConverters = new object[MaxTypeCode + 1]
+ {
+ null,
+ null,
+ null,
+ new JsonValueConverterBooleanNullable(),
+ new JsonValueConverterCharNullable(),
+ new JsonValueConverterSByteNullable(),
+ new JsonValueConverterByteNullable(),
+ new JsonValueConverterInt16Nullable(),
+ new JsonValueConverterUInt16Nullable(),
+ new JsonValueConverterInt32Nullable(),
+ new JsonValueConverterUInt32Nullable(),
+ new JsonValueConverterInt64Nullable(),
+ new JsonValueConverterUInt64Nullable(),
+ new JsonValueConverterSingleNullable(),
+ new JsonValueConverterDoubleNullable(),
+ new JsonValueConverterDecimalNullable(),
+ new JsonValueConverterDateTimeNullable(),
+ null,
+ new JsonValueConverterString()
+ };
+
+ internal static object GetDefaultPropertyValueConverter(Type propertyType, bool isNullable)
+ {
+ object converter = null;
+
+ int typeCode = (int)Type.GetTypeCode(propertyType);
+ if (typeCode <= MaxTypeCode)
+ {
+ if (isNullable)
+ {
+ converter = s_NullableConverters[typeCode];
+ }
+ else
+ {
+ converter = s_Converters[typeCode];
+ }
+ }
+
+ return converter;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class DefaultEnumConverter<TValue> : JsonValueConverter<TValue>
+ {
+ public bool TreatAsString { get; private set; }
+
+ internal DefaultEnumConverter(bool treatAsString)
+ {
+ TreatAsString = treatAsString;
+ }
+
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out TValue value)
+ {
+ if (TreatAsString)
+ {
+ // Assume the token is a string
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ value = default;
+ return false;
+ }
+
+#if !BUILDING_INBOX_LIBRARY
+ // todo: add code here to handle NS2.0
+ throw new NotImplementedException(SR.EnumConverterNotImplemented);
+#else
+ string enumString = reader.GetString();
+ if (!Enum.TryParse(valueType, enumString, out object objValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (TValue)objValue;
+ return true;
+#endif
+ }
+
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetUInt64(out ulong ulongValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (TValue)Enum.ToObject(valueType, ulongValue);
+ return true;
+ }
+
+ public override void Write(TValue value, ref Utf8JsonWriter writer)
+ {
+ if (TreatAsString)
+ {
+ writer.WriteStringValue(value.ToString());
+ }
+ else
+ {
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+
+ if (underlyingType == typeof(ulong))
+ {
+ // Keep +sign
+ ulong ulongValue = Convert.ToUInt64(value);
+ writer.WriteNumberValue(ulongValue);
+ }
+ else
+ {
+ // long can hold the signed\unsigned values of other integer types
+ long longValue = Convert.ToInt64(value);
+ writer.WriteNumberValue(longValue);
+ }
+ }
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, TValue value, ref Utf8JsonWriter writer)
+ {
+ if (TreatAsString)
+ {
+ writer.WriteString(escapedPropertyName, value.ToString());
+ }
+ else
+ {
+ Type underlyingType = Enum.GetUnderlyingType(value.GetType());
+
+ if (underlyingType == typeof(ulong))
+ {
+ // Use the ulong converter to prevent conversion into a signed\long value.
+ ulong ulongValue = Convert.ToUInt64(value);
+ writer.WriteNumber(escapedPropertyName, ulongValue);
+ }
+ else
+ {
+ // long can hold the signed\unsigned values of other integer types.
+ long longValue = Convert.ToInt64(value);
+ writer.WriteNumber(escapedPropertyName, longValue);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterBoolean : JsonValueConverter<bool>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out bool value)
+ {
+ if (reader.TokenType != JsonTokenType.True && reader.TokenType != JsonTokenType.False)
+ {
+ value = default;
+ return false;
+ }
+
+ value = reader.GetBoolean();
+ return true;
+ }
+
+ public override void Write(bool value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteBooleanValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, bool value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteBoolean(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterBooleanNullable : JsonValueConverter<bool?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out bool? value)
+ {
+ if (reader.TokenType != JsonTokenType.True && reader.TokenType != JsonTokenType.False)
+ {
+ value = default;
+ return false;
+ }
+
+ value = reader.GetBoolean();
+ return true;
+ }
+
+ public override void Write(bool? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteBooleanValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, bool? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteBoolean(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterByte : JsonValueConverter<byte>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out byte value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, byte.MinValue, byte.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (byte)rawValue;
+ return true;
+ }
+
+ public override void Write(byte value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, byte value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterByteNullable : JsonValueConverter<byte?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out byte? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, byte.MinValue, byte.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (byte?)rawValue;
+ return true;
+ }
+
+ public override void Write(byte? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, byte? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+using System.Runtime.InteropServices;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterChar : JsonValueConverter<char>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out char value)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ value = default;
+ return false;
+ }
+
+ value = reader.GetString()[0];
+ return true;
+ }
+
+ public override void Write(char value, ref Utf8JsonWriter writer)
+ {
+#if BUILDING_INBOX_LIBRARY
+ Span<char> temp = MemoryMarshal.CreateSpan<char>(ref value, 1);
+ writer.WriteStringValue(temp);
+#else
+ writer.WriteStringValue(value.ToString());
+#endif
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, char value, ref Utf8JsonWriter writer)
+ {
+#if BUILDING_INBOX_LIBRARY
+ Span<char> temp = MemoryMarshal.CreateSpan<char>(ref value, 1);
+ writer.WriteString(escapedPropertyName, temp);
+#else
+ writer.WriteString(escapedPropertyName, value.ToString());
+#endif
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterCharNullable : JsonValueConverter<char?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out char? value)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ value = default;
+ return false;
+ }
+
+ value = reader.GetString()[0];
+ return true;
+ }
+
+ public override void Write(char? value, ref Utf8JsonWriter writer)
+ {
+ Debug.Assert(value.HasValue); // nulls are filtered before calling here
+#if BUILDING_INBOX_LIBRARY
+ char tempChar = value.Value;
+ Span<char> tempSpan = MemoryMarshal.CreateSpan<char>(ref tempChar, 1);
+ writer.WriteStringValue(tempSpan);
+#else
+ writer.WriteStringValue(value.ToString());
+#endif
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, char? value, ref Utf8JsonWriter writer)
+ {
+ Debug.Assert(value.HasValue); // nulls are filtered before calling here
+#if BUILDING_INBOX_LIBRARY
+ char tempChar = value.Value;
+ Span<char> tempSpan = MemoryMarshal.CreateSpan<char>(ref tempChar, 1);
+ writer.WriteString(escapedPropertyName, tempSpan);
+#else
+ writer.WriteString(escapedPropertyName, value.ToString());
+#endif
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Buffers.Text;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterDateTime : JsonValueConverter<DateTime>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out DateTime value)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ value = default;
+ return false;
+ }
+
+ ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
+ return Utf8Parser.TryParse(span, out value, out int bytesConsumed, 'O') && span.Length == bytesConsumed;
+ }
+
+ public override void Write(DateTime value, ref Utf8JsonWriter writer)
+ {
+ // todo: use the appropriate DateTime method once available (https://github.com/dotnet/corefx/issues/34690)
+ writer.WriteStringValue(value.ToString("O"));
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, DateTime value, ref Utf8JsonWriter writer)
+ {
+ // todo: use the appropriate DateTime method once available (https://github.com/dotnet/corefx/issues/34690)
+ writer.WriteString(escapedPropertyName, value.ToString("O"));
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Buffers.Text;
+using System.Diagnostics;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterDateTimeNullable : JsonValueConverter<DateTime?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out DateTime? value)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ value = default;
+ return false;
+ }
+
+ ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
+ bool success = Utf8Parser.TryParse(span, out DateTime tempValue, out int bytesConsumed, 'O') && span.Length == bytesConsumed;
+ value = tempValue;
+ return success;
+ }
+
+ public override void Write(DateTime? value, ref Utf8JsonWriter writer)
+ {
+ Debug.Assert(value.HasValue); // nulls are filtered before calling here
+
+ // todo: use the appropriate DateTime method once available.
+ writer.WriteStringValue(value.Value.ToString("O"));
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, DateTime? value, ref Utf8JsonWriter writer)
+ {
+ Debug.Assert(value.HasValue); // nulls are filtered before calling here
+
+ // todo: use the appropriate DateTime method once available.
+ writer.WriteString(escapedPropertyName, value.Value.ToString("O"));
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterDecimal : JsonValueConverter<decimal>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out decimal value)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ {
+ value = default;
+ return false;
+ }
+
+ return reader.TryGetDecimal(out value);
+ }
+
+ public override void Write(decimal value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, decimal value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterDecimalNullable : JsonValueConverter<decimal?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out decimal? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number
+ || !reader.TryGetDecimal(out decimal rawValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = rawValue;
+ return true;
+ }
+
+ public override void Write(decimal? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, decimal? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterDouble : JsonValueConverter<double>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out double value)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ {
+ value = default;
+ return false;
+ }
+
+ return reader.TryGetDouble(out value);
+ }
+
+ public override void Write(double value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, double value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterDoubleNullable : JsonValueConverter<double?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out double? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetDouble(out double rawValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = rawValue;
+ return true;
+ }
+
+ public override void Write(double? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, double? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterInt16 : JsonValueConverter<short>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out short value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, short.MinValue, short.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (short)rawValue;
+ return true;
+ }
+
+ public override void Write(short value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, short value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterInt16Nullable : JsonValueConverter<short?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out short? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, short.MinValue, short.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (short?)rawValue;
+ return true;
+ }
+
+ public override void Write(short? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, short? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterInt32 : JsonValueConverter<int>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out int value)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ {
+ value = default;
+ return false;
+ }
+
+ return reader.TryGetInt32(out value);
+ }
+
+ public override void Write(int value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, int value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterInt32Nullable : JsonValueConverter<int?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out int? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = rawValue;
+ return true;
+ }
+
+ public override void Write(int? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, int? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterInt64 : JsonValueConverter<long>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out long value)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ {
+ value = default;
+ return false;
+ }
+
+ return reader.TryGetInt64(out value);
+ }
+
+ public override void Write(long value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, long value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterInt64Nullable : JsonValueConverter<long?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out long? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt64(out long rawValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = rawValue;
+ return true;
+ }
+
+ public override void Write(long? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, long? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterSByte : JsonValueConverter<sbyte>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out sbyte value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, sbyte.MinValue, sbyte.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (sbyte)reader.GetInt32();
+ return true;
+ }
+
+ public override void Write(sbyte value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, sbyte value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterSByteNullable : JsonValueConverter<sbyte?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out sbyte? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, sbyte.MinValue, sbyte.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (sbyte?)rawValue;
+ return true;
+ }
+
+ public override void Write(sbyte? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, sbyte? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterSingle : JsonValueConverter<float>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out float value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetDouble(out double rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, float.MinValue, float.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (float)rawValue;
+ return true;
+ }
+
+ public override void Write(float value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, float value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterSingleNullable : JsonValueConverter<float?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out float? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetDouble(out double rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, float.MinValue, float.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (float?)rawValue;
+ return true;
+ }
+
+ public override void Write(float? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, float? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterString : JsonValueConverter<string>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out string value)
+ {
+ if (reader.TokenType != JsonTokenType.String)
+ {
+ value = default;
+ return false;
+ }
+
+ value = reader.GetString();
+ return true;
+ }
+
+ public override void Write(string value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteStringValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, string value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteString(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterUInt16 : JsonValueConverter<ushort>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out ushort value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, ushort.MinValue, ushort.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (ushort)rawValue;
+ return true;
+ }
+
+ public override void Write(ushort value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, ushort value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterUInt16Nullable : JsonValueConverter<ushort?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out ushort? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetInt32(out int rawValue) ||
+ !JsonHelpers.IsInRangeInclusive(rawValue, ushort.MinValue, ushort.MaxValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = (ushort?)rawValue;
+ return true;
+ }
+
+ public override void Write(ushort? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, ushort? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterUInt32 : JsonValueConverter<uint>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out uint value)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ {
+ value = default;
+ return false;
+ }
+
+ return reader.TryGetUInt32(out value);
+ }
+
+ public override void Write(uint value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, uint value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterUInt32Nullable : JsonValueConverter<uint?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out uint? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetUInt32(out uint rawValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = rawValue;
+ return true;
+ }
+
+ public override void Write(uint? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, uint? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterUInt64 : JsonValueConverter<ulong>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out ulong value)
+ {
+ if (reader.TokenType != JsonTokenType.Number)
+ {
+ value = default;
+ return false;
+ }
+
+ return reader.TryGetUInt64(out value);
+ }
+
+ public override void Write(ulong value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, ulong value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization.Converters
+{
+ internal sealed class JsonValueConverterUInt64Nullable : JsonValueConverter<ulong?>
+ {
+ public override bool TryRead(Type valueType, ref Utf8JsonReader reader, out ulong? value)
+ {
+ if (reader.TokenType != JsonTokenType.Number ||
+ !reader.TryGetUInt64(out ulong rawValue))
+ {
+ value = default;
+ return false;
+ }
+
+ value = rawValue;
+ return true;
+ }
+
+ public override void Write(ulong? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumberValue(value.Value);
+ }
+
+ public override void Write(Span<byte> escapedPropertyName, ulong? value, ref Utf8JsonWriter writer)
+ {
+ writer.WriteNumber(escapedPropertyName, value.Value);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Reflection;
+
+namespace System.Text.Json.Serialization
+{
+ internal partial class JsonClassInfo
+ {
+ private void AddProperty(Type propertyType, PropertyInfo propertyInfo, Type classType, JsonSerializerOptions options)
+ {
+ Type collectionElementType = null;
+ ClassType propertyClassType = GetClassType(propertyType);
+ if (propertyClassType == ClassType.Enumerable)
+ {
+ collectionElementType = GetElementType(propertyType);
+ // todo: if collectionElementType is object, create loosely-typed collection (JsonArray).
+ }
+
+ // Create the JsonPropertyInfo<TType, TProperty>
+ Type genericPropertyType = typeof(JsonPropertyInfo<,>).MakeGenericType(classType, propertyType);
+ JsonPropertyInfo jsonInfo = (JsonPropertyInfo)Activator.CreateInstance(
+ genericPropertyType,
+ BindingFlags.Instance | BindingFlags.NonPublic,
+ binder: null,
+ new object[] { classType, propertyType, propertyInfo, collectionElementType, options },
+ culture: null);
+
+ if (propertyInfo != null)
+ {
+ string propertyName = propertyInfo.Name;
+
+ // At this point propertyName is valid UTF16, so just call the simple UTF16->UTF8 encoder.
+ byte[] propertyNameBytes = Encoding.UTF8.GetBytes(propertyName);
+ jsonInfo._name = propertyNameBytes;
+
+ // Cache the escaped name.
+ int valueIdx = JsonWriterHelper.NeedsEscaping(propertyNameBytes);
+ if (valueIdx == -1)
+ {
+ jsonInfo._escapedName = propertyNameBytes;
+ }
+ else
+ {
+ int length = JsonWriterHelper.GetMaxEscapedLength(propertyNameBytes.Length, valueIdx);
+
+ byte[] tempArray = ArrayPool<byte>.Shared.Rent(length);
+
+ JsonWriterHelper.EscapeString(propertyNameBytes, tempArray, valueIdx, out int written);
+ jsonInfo._escapedName = new byte[written];
+ tempArray.CopyTo(jsonInfo._escapedName, 0);
+
+ // We clear the array because it is "user data" (although a property name).
+ ArrayPool<byte>.Shared.Return(tempArray, clearArray: true);
+ }
+
+ _property_refs.Add(new PropertyRef(GetKey(propertyNameBytes), jsonInfo));
+ }
+ else
+ {
+ // A single property or an IEnumerable
+ _property_refs.Add(new PropertyRef(0, jsonInfo));
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace System.Text.Json.Serialization
+{
+ internal sealed partial class JsonClassInfo
+ {
+ // The length of the property name embedded in the key (in bytes).
+ private const int PropertyNameKeyLength = 6;
+
+ private readonly List<PropertyRef> _property_refs = new List<PropertyRef>();
+ private readonly List<PropertyRef> _property_refs_sorted = new List<PropertyRef>();
+
+ internal delegate object ConstructorDelegate();
+ internal ConstructorDelegate CreateObject { get; private set; }
+
+ internal ClassType ClassType { get; private set; }
+
+ // If enumerable, the JsonClassInfo for the element type.
+ internal JsonClassInfo ElementClassInfo { get; private set; }
+
+ public Type Type { get; private set; }
+
+ internal JsonClassInfo(Type type, JsonSerializerOptions options)
+ {
+ Type = type;
+ ClassType = GetClassType(type);
+
+ CreateObject = options.ClassMaterializerStrategy.CreateConstructor(type);
+
+ // Ignore properties on enumerable.
+ if (ClassType == ClassType.Object)
+ {
+ foreach (PropertyInfo propertyInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
+ {
+ // For now we only support public getters\setters
+ if (propertyInfo.GetMethod?.IsPublic == true ||
+ propertyInfo.SetMethod?.IsPublic == true)
+ {
+ AddProperty(propertyInfo.PropertyType, propertyInfo, type, options);
+ }
+ }
+ }
+ else if (ClassType == ClassType.Enumerable)
+ {
+ // Add a single property that maps to the class type so we can have policies applied.
+ AddProperty(type, propertyInfo : null, type, options);
+
+ // Create a ClassInfo that maps to the element type which is used for (de)serialization and policies.
+ Type elementType = GetElementType(type);
+ ElementClassInfo = options.GetOrAddClass(elementType);
+ }
+ else
+ {
+ Debug.Assert(ClassType == ClassType.Value);
+
+ // Add a single property that maps to the class type so we can have policies applied.
+ AddProperty(type, propertyInfo: null, type, options);
+ }
+ }
+
+ internal JsonPropertyInfo GetProperty(ReadOnlySpan<byte> propertyName, int propertyIndex)
+ {
+ ulong key = GetKey(propertyName);
+ JsonPropertyInfo info = null;
+
+ // First try sorted lookup.
+ int count = _property_refs_sorted.Count;
+ if (count != 0)
+ {
+ int iForward = propertyIndex;
+ int iBackward = propertyIndex - 1;
+ while (iForward < count || (iBackward >= 0 && iBackward < count))
+ {
+ if (iForward < count)
+ {
+ if (TryIsPropertyRefEqual(_property_refs_sorted, propertyName, key, iForward, out info))
+ {
+ return info;
+ }
+ ++iForward;
+ }
+
+ if (iBackward >= 0)
+ {
+ if (TryIsPropertyRefEqual(_property_refs_sorted, propertyName, key, iBackward, out info))
+ {
+ return info;
+ }
+ --iBackward;
+ }
+ }
+ }
+
+ // Then try fallback
+ for (int i = 0; i < _property_refs.Count; i++)
+ {
+ if (TryIsPropertyRefEqual(_property_refs, propertyName, key, i, out info))
+ {
+ break;
+ }
+ }
+
+ if (info != null)
+ {
+ _property_refs_sorted.Add(new PropertyRef(key, info));
+ }
+
+ return info;
+ }
+
+ internal JsonPropertyInfo GetPolicyProperty()
+ {
+ Debug.Assert(_property_refs.Count == 1);
+ return _property_refs[0].Info;
+ }
+
+ internal JsonPropertyInfo GetProperty(int index)
+ {
+ Debug.Assert(index < _property_refs.Count);
+ return _property_refs[index].Info;
+ }
+
+ internal int PropertyCount
+ {
+ get
+ {
+ return _property_refs.Count;
+ }
+ }
+
+ private static bool TryIsPropertyRefEqual(List<PropertyRef> list, ReadOnlySpan<byte> propertyName, ulong key, int index, out JsonPropertyInfo info)
+ {
+ if (key == list[index].Key)
+ {
+ if (propertyName.Length <= PropertyNameKeyLength ||
+ // We compare the whole name, although we could skip the first 6 bytes (but it's likely not any faster)
+ propertyName.SequenceEqual((ReadOnlySpan<byte>)list[index].Info._name))
+ {
+ info = list[index].Info;
+ return true;
+ }
+ }
+
+ info = null;
+ return false;
+ }
+
+ private static ulong GetKey(ReadOnlySpan<byte> propertyName)
+ {
+ Debug.Assert(propertyName.Length > 0);
+
+ ulong key;
+ int length = propertyName.Length;
+
+ // Embed the propertyName in the first 6 bytes of the key.
+ if (length > 3)
+ {
+ key = MemoryMarshal.Read<uint>(propertyName);
+ if (length > 4)
+ {
+ key |= (ulong)propertyName[4] << 32;
+ }
+ if (length > 5)
+ {
+ key |= (ulong)propertyName[5] << 40;
+ }
+ }
+ else if (length > 1)
+ {
+ key = MemoryMarshal.Read<ushort>(propertyName);
+ if (length > 2)
+ {
+ key |= (ulong)propertyName[2] << 16;
+ }
+ }
+ else
+ {
+ key = propertyName[0];
+ }
+
+ // Embed the propertyName length in the last two bytes.
+ key |= (ulong)propertyName.Length << 48;
+ return key;
+
+ }
+
+ private static Type GetElementType(Type propertyType)
+ {
+ Type elementType = null;
+ if (typeof(IEnumerable).IsAssignableFrom(propertyType))
+ {
+ elementType = propertyType.GetElementType();
+ if (elementType == null)
+ {
+ if (propertyType.IsGenericType)
+ {
+ elementType = propertyType.GetGenericArguments()[0];
+ }
+ else
+ {
+ // Unable to determine collection type; attempt to use object which will be used to create loosely-typed collection.
+ return typeof(object);
+ }
+ }
+ }
+
+ return elementType;
+ }
+
+ internal static ClassType GetClassType(Type type)
+ {
+ Debug.Assert(type != null);
+
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ type = Nullable.GetUnderlyingType(type);
+ }
+
+ // A Type is considered a value if it implements IConvertible.
+ if (typeof(IConvertible).IsAssignableFrom(type))
+ return ClassType.Value;
+
+ if (typeof(IEnumerable).IsAssignableFrom(type))
+ return ClassType.Enumerable;
+
+ return ClassType.Object;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+
+namespace System.Text.Json.Serialization.Policies
+{
+ internal abstract class JsonEnumerableConverter
+ {
+ public abstract IEnumerable CreateFromList(Type elementType, IList sourceList);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using System.Text.Json.Serialization.Converters;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization
+{
+ internal abstract class JsonPropertyInfo
+ {
+ // For now, just a global converter.
+ private static JsonEnumerableConverter s_jsonEnumerableConverter = new DefaultArrayConverter();
+
+ internal ClassType ClassType;
+
+ internal byte[] _name = default;
+ internal byte[] _escapedName = default;
+
+ internal bool HasGetter { get; set; }
+ internal bool HasSetter { get; set; }
+
+ public ReadOnlySpan<byte> EscapedName => _escapedName;
+ public ReadOnlySpan<byte> Name => _name;
+
+ // todo: to minimize hashtable lookups, cache JsonClassInfo:
+ //public JsonClassInfo ClassInfo;
+
+ // Constructor used for internal identifiers
+ internal JsonPropertyInfo() { }
+
+ internal JsonPropertyInfo(Type parentClassType, Type propertyType, PropertyInfo propertyInfo, Type elementType, JsonSerializerOptions options)
+ {
+ ParentClassType = parentClassType;
+ PropertyType = propertyType;
+ PropertyInfo = propertyInfo;
+ ClassType = JsonClassInfo.GetClassType(propertyType);
+ if (elementType != null)
+ {
+ ElementClassInfo = options.GetOrAddClass(elementType);
+ }
+
+ IsNullableType = propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
+ CanBeNull = IsNullableType || !propertyType.IsValueType;
+ }
+
+ internal JsonEnumerableConverter EnumerableConverter { get; private set; }
+
+ public PropertyInfo PropertyInfo { get; private set; }
+
+ internal Type PropertyType { get; private set; }
+
+ internal Type ParentClassType { get; private set; }
+
+ internal JsonClassInfo ElementClassInfo { get; private set; }
+
+ internal bool IsNullableType { get; private set; }
+
+ internal bool CanBeNull { get; private set; }
+
+ internal bool IgnoreNullPropertyValueOnRead(JsonSerializerOptions options)
+ {
+ return options.IgnoreNullPropertyValueOnRead;
+ }
+
+ internal bool IgnoreNullPropertyValueOnWrite(JsonSerializerOptions options)
+ {
+ return options.IgnoreNullPropertyValueOnWrite;
+ }
+
+ internal abstract object GetValueAsObject(object obj, JsonSerializerOptions options);
+ internal abstract void SetValueAsObject(object obj, object value, JsonSerializerOptions options);
+
+ internal abstract void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
+
+ internal abstract void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader);
+
+ internal abstract void Write(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer);
+
+ internal abstract void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer);
+
+ internal virtual void GetPolicies(JsonSerializerOptions options)
+ {
+ if (PropertyType.IsArray)
+ {
+ EnumerableConverter = s_jsonEnumerableConverter;
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Reflection;
+
+namespace System.Text.Json.Serialization
+{
+ /// <summary>
+ /// Represents a strongly-typed property to prevent boxing and to create a direct delegate to the getter\setter.
+ /// </summary>
+ internal class JsonPropertyInfo<TClass, TProperty> : JsonPropertyInfo<TProperty>
+ {
+ private bool _isPropertyPolicy;
+ internal Func<TClass, TProperty> Get { get; private set; }
+ internal Action<TClass, TProperty> Set { get; private set; }
+
+ // Constructor used for internal identifiers
+ internal JsonPropertyInfo() { }
+
+ internal JsonPropertyInfo(Type classType, Type propertyType, PropertyInfo propertyInfo, Type elementType, JsonSerializerOptions options) :
+ base(classType, propertyType, propertyInfo, elementType, options)
+ {
+ if (propertyInfo != null)
+ {
+ if (propertyInfo.GetMethod?.IsPublic == true)
+ {
+ HasGetter = true;
+ Get = (Func<TClass, TProperty>)Delegate.CreateDelegate(typeof(Func<TClass, TProperty>), propertyInfo.GetGetMethod());
+ }
+
+ if (propertyInfo.SetMethod?.IsPublic == true)
+ {
+ HasSetter = true;
+ Set = (Action<TClass, TProperty>)Delegate.CreateDelegate(typeof(Action<TClass, TProperty>), propertyInfo.GetSetMethod());
+ }
+ }
+ else
+ {
+ _isPropertyPolicy = true;
+ HasGetter = true;
+ HasSetter = true;
+ }
+
+ GetPolicies(options);
+ }
+
+ internal override object GetValueAsObject(object obj, JsonSerializerOptions options)
+ {
+ if (_isPropertyPolicy)
+ {
+ return obj;
+ }
+
+ Debug.Assert(Get != null);
+ return Get((TClass)obj);
+ }
+
+ internal override void SetValueAsObject(object obj, object value, JsonSerializerOptions options)
+ {
+ Debug.Assert(Set != null);
+ TProperty typedValue = (TProperty)value;
+
+ if (typedValue != null || !IgnoreNullPropertyValueOnWrite(options))
+ {
+ Set((TClass)obj, (TProperty)value);
+ }
+ }
+
+ internal override void Read(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
+ {
+ if (ElementClassInfo != null)
+ {
+ // Forward the setter to the value-based JsonPropertyInfo.
+ JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
+ propertyInfo.ReadEnumerable(tokenType, options, ref state, ref reader);
+ }
+ else if (HasSetter)
+ {
+ if (ValueConverter != null)
+ {
+ Type propertyType = PropertyType;
+ if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ propertyType = Nullable.GetUnderlyingType(propertyType);
+ }
+
+ if (ValueConverter.TryRead(propertyType, ref reader, out TProperty value))
+ {
+ if (state.Current.ReturnValue == null)
+ {
+ state.Current.ReturnValue = value;
+ }
+ else
+ {
+ if (value != null || !IgnoreNullPropertyValueOnRead(options))
+ {
+ Set((TClass)state.Current.ReturnValue, value);
+ }
+ }
+
+ return;
+ }
+ }
+
+ ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(PropertyType, reader, state);
+ }
+ }
+
+ internal override void ReadEnumerable(JsonTokenType tokenType, JsonSerializerOptions options, ref ReadStack state, ref Utf8JsonReader reader)
+ {
+ if (ValueConverter != null)
+ {
+ Type propertyType = PropertyType;
+ if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ propertyType = Nullable.GetUnderlyingType(propertyType);
+ }
+
+ if (ValueConverter.TryRead(propertyType, ref reader, out TProperty value))
+ {
+ ReadStackFrame.SetReturnValue(value, options, ref state.Current);
+ return;
+ }
+ }
+
+ ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(PropertyType, reader, state);
+ }
+
+ // todo: have the caller check if current.Enumerator != null and call WriteEnumerable of the underlying property directly to avoid an extra virtual call.
+ internal override void Write(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer)
+ {
+ if (current.Enumerator != null)
+ {
+ // Forward the setter to the value-based JsonPropertyInfo.
+ JsonPropertyInfo propertyInfo = ElementClassInfo.GetPolicyProperty();
+ propertyInfo.WriteEnumerable(options, ref current, ref writer);
+ }
+ else if (HasGetter)
+ {
+ TProperty value;
+ if (_isPropertyPolicy)
+ {
+ value = (TProperty)current.CurrentValue;
+ }
+ else
+ {
+ value = Get((TClass)current.CurrentValue);
+ }
+
+ if (value == null)
+ {
+ if (_escapedName == null)
+ {
+ writer.WriteNullValue();
+ }
+ else if (!IgnoreNullPropertyValueOnWrite(options))
+ {
+ writer.WriteNull(_escapedName);
+ }
+ }
+ else if (ValueConverter != null)
+ {
+ if (_escapedName != null)
+ {
+ ValueConverter.Write(_escapedName, value, ref writer);
+ }
+ else
+ {
+ ValueConverter.Write(value, ref writer);
+ }
+ }
+ }
+ }
+
+ internal override void WriteEnumerable(JsonSerializerOptions options, ref WriteStackFrame current, ref Utf8JsonWriter writer)
+ {
+ if (ValueConverter != null)
+ {
+ Debug.Assert(current.Enumerator != null);
+ TProperty value = (TProperty)current.Enumerator.Current;
+ if (value == null)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ ValueConverter.Write(value, ref writer);
+ }
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Reflection;
+using System.Text.Json.Serialization.Converters;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization
+{
+ internal abstract class JsonPropertyInfo<TProperty> : JsonPropertyInfo
+ {
+ // For now, just a global converter.
+ private static readonly JsonValueConverter<TProperty> s_enumConverter = new DefaultEnumConverter<TProperty>(false);
+
+ // Constructor used for internal identifiers
+ internal JsonPropertyInfo() { }
+
+ internal JsonPropertyInfo(Type classType, Type propertyType, PropertyInfo propertyInfo, Type elementType, JsonSerializerOptions options) :
+ base(classType, propertyType, propertyInfo, elementType, options)
+ { }
+
+ public JsonValueConverter<TProperty> ValueConverter { get; internal set; }
+
+ internal override void GetPolicies(JsonSerializerOptions options)
+ {
+ Type propertyType = PropertyType;
+ bool isNullable = propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
+ if (isNullable)
+ {
+ propertyType = Nullable.GetUnderlyingType(propertyType);
+ }
+
+ // For Enums, support both the type Enum plus strongly-typed Enums.
+ if (propertyType.IsEnum || propertyType == typeof(Enum))
+ {
+ ValueConverter = s_enumConverter;
+ }
+ else
+ {
+ ValueConverter = (JsonValueConverter<TProperty>)DefaultConverters.GetDefaultPropertyValueConverter(propertyType, isNullable);
+ }
+
+ base.GetPolicies(options);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Diagnostics;
+using System.Text.Json.Serialization.Policies;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static void HandleStartArray(
+ JsonSerializerOptions options,
+ ref Utf8JsonReader reader,
+ ref ReadStack state)
+ {
+ if (state.Current.Skip())
+ {
+ // The array is not being applied to the object.
+ state.Push();
+ state.Current.Drain = true;
+ return;
+ }
+
+ Type arrayType = state.Current.JsonPropertyInfo.PropertyType;
+ if (!typeof(IEnumerable).IsAssignableFrom(arrayType) || (arrayType.IsArray && arrayType.GetArrayRank() > 1))
+ {
+ ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(arrayType, reader, state);
+ }
+
+ Debug.Assert(state.Current.IsPropertyEnumerable());
+ if (state.Current.IsPropertyEnumerable())
+ {
+ if (state.Current.EnumerableCreated)
+ {
+ // A nested json array so push a new stack frame.
+ Type elementType = state.Current.JsonClassInfo.ElementClassInfo.GetPolicyProperty().PropertyType;
+
+ state.Push();
+
+ state.Current.JsonClassInfo = options.GetOrAddClass(elementType);
+ state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetPolicyProperty();
+ state.Current.PopStackOnEndArray = true;
+ }
+ else
+ {
+ state.Current.EnumerableCreated = true;
+ }
+
+ // If current property is already set (from a constructor, for example) leave as-is
+ if (state.Current.JsonPropertyInfo.GetValueAsObject(state.Current.ReturnValue, options) == null)
+ {
+ // Create the enumerable.
+ object value = ReadStackFrame.CreateEnumerableValue(ref reader, ref state, options);
+ if (value != null)
+ {
+ if (state.Current.ReturnValue != null)
+ {
+ state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, value, options);
+ }
+ else
+ {
+ // Primitive arrays being returned without object
+ state.Current.SetReturnValue(value, options);
+ }
+ }
+ }
+ }
+ }
+
+ private static bool HandleEndArray(
+ JsonSerializerOptions options,
+ ref ReadStack state)
+ {
+ bool lastFrame = state.IsLastFrame;
+
+ if (state.Current.Drain)
+ {
+ // The array is not being applied to the object.
+ state.Pop();
+ return lastFrame;
+ }
+
+ IEnumerable value = ReadStackFrame.GetEnumerableValue(state.Current);
+ if (value == null)
+ {
+ // We added the items to the list property already.
+ state.Current.ResetProperty();
+ return false;
+ }
+
+ bool setPropertyDirectly;
+ if (state.Current.TempEnumerableValues != null)
+ {
+ JsonEnumerableConverter converter = state.Current.JsonPropertyInfo.EnumerableConverter;
+ Debug.Assert(converter != null);
+
+ Type elementType = state.Current.GetElementType();
+ value = converter.CreateFromList(elementType, (IList)value);
+ setPropertyDirectly = true;
+ }
+ else
+ {
+ setPropertyDirectly = false;
+ }
+
+ bool valueReturning = state.Current.PopStackOnEndArray;
+ if (state.Current.PopStackOnEndArray)
+ {
+ state.Pop();
+ }
+
+ if (lastFrame)
+ {
+ if (state.Current.ReturnValue == null)
+ {
+ // Returning a converted list or object.
+ state.Current.Reset();
+ state.Current.ReturnValue = value;
+ return true;
+ }
+ else if (state.Current.IsEnumerable())
+ {
+ // Returning a non-converted list.
+ return true;
+ }
+ // else there must be an outer object, so we'll return false here.
+ }
+
+ ReadStackFrame.SetReturnValue(value, options, ref state.Current, setPropertyDirectly: setPropertyDirectly);
+
+ if (!valueReturning)
+ {
+ state.Current.ResetProperty();
+ }
+
+ return false;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static bool HandleNull(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
+ {
+ if (state.Current.Skip())
+ {
+ return false;
+ }
+
+ JsonPropertyInfo propertyInfo = state.Current.JsonPropertyInfo;
+ if (!propertyInfo.CanBeNull)
+ {
+ ThrowHelper.ThrowJsonReaderException_DeserializeCannotBeNull(reader, state);
+ }
+
+ if (state.Current.IsEnumerable() || state.Current.IsPropertyEnumerable())
+ {
+ ReadStackFrame.SetReturnValue(null, options, ref state.Current);
+ return false;
+ }
+
+ if (state.Current.ReturnValue == null)
+ {
+ return true;
+ }
+
+ if (!propertyInfo.IgnoreNullPropertyValueOnRead(options))
+ {
+ state.Current.JsonPropertyInfo.SetValueAsObject(state.Current.ReturnValue, null, options);
+ }
+
+ return false;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static void HandleStartObject(JsonSerializerOptions options, ref ReadStack state)
+ {
+ if (state.Current.Skip())
+ {
+ state.Push();
+ state.Current.Drain = true;
+ return;
+ }
+
+ if (state.Current.IsEnumerable() || state.Current.IsPropertyEnumerable())
+ {
+ // An array of objects either on the current property or on a list
+ Type objType = state.Current.GetElementType();
+ state.Push();
+ state.Current.JsonClassInfo = options.GetOrAddClass(objType);
+ }
+ else if (state.Current.JsonPropertyInfo != null)
+ {
+ // Nested object
+ Type objType = state.Current.JsonPropertyInfo.PropertyType;
+ state.Push();
+ state.Current.JsonClassInfo = options.GetOrAddClass(objType);
+ }
+
+ JsonClassInfo classInfo = state.Current.JsonClassInfo;
+ state.Current.ReturnValue = classInfo.CreateObject();
+ }
+
+ private static bool HandleEndObject(JsonSerializerOptions options, ref ReadStack state)
+ {
+ bool isLastFrame = state.IsLastFrame;
+ if (state.Current.Drain)
+ {
+ state.Pop();
+ return isLastFrame;
+ }
+
+ object value = state.Current.ReturnValue;
+
+ if (isLastFrame)
+ {
+ state.Current.Reset();
+ state.Current.ReturnValue = value;
+ return true;
+ }
+
+ state.Pop();
+ ReadStackFrame.SetReturnValue(value, options, ref state.Current);
+ return false;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static bool HandleValue(JsonTokenType tokenType, JsonSerializerOptions options, ref Utf8JsonReader reader, ref ReadStack state)
+ {
+ if (state.Current.Skip())
+ {
+ return false;
+ }
+
+ bool lastCall = (!state.Current.IsEnumerable() && !state.Current.IsPropertyEnumerable() && state.Current.ReturnValue == null);
+ state.Current.JsonPropertyInfo.Read(tokenType, options, ref state, ref reader);
+ return lastCall;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ public static TValue Parse<TValue>(ReadOnlySpan<byte> utf8Json, JsonSerializerOptions options = null)
+ {
+ if (utf8Json == null)
+ throw new ArgumentNullException(nameof(utf8Json));
+
+ return (TValue)ParseCore(utf8Json, typeof(TValue), options);
+ }
+
+ public static object Parse(ReadOnlySpan<byte> utf8Json, Type returnType, JsonSerializerOptions options = null)
+ {
+ if (utf8Json == null)
+ throw new ArgumentNullException(nameof(utf8Json));
+
+ return ParseCore(utf8Json, returnType, options);
+ }
+
+ private static object ParseCore(ReadOnlySpan<byte> utf8Json, Type returnType, JsonSerializerOptions options)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ var readerState = new JsonReaderState(options: options.ReaderOptions);
+ var reader = new Utf8JsonReader(utf8Json, isFinalBlock: true, readerState);
+ object result = ReadCore(returnType, options, ref reader);
+
+ readerState = reader.CurrentState;
+ if (readerState.BytesConsumed != utf8Json.Length)
+ {
+ throw new JsonReaderException(SR.Format(SR.DeserializeDataRemaining,
+ utf8Json.Length, utf8Json.Length - readerState.BytesConsumed), readerState);
+ }
+
+ return result;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private const int HalfMaxValue = int.MaxValue / 2;
+
+ public static ValueTask<TValue> ReadAsync<TValue>(Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
+ {
+ if (utf8Json == null)
+ throw new ArgumentNullException(nameof(utf8Json));
+
+ return ReadAsync<TValue>(utf8Json, typeof(TValue), options, cancellationToken);
+ }
+
+ public static ValueTask<object> ReadAsync(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
+ {
+ if (utf8Json == null)
+ throw new ArgumentNullException(nameof(utf8Json));
+
+ if (returnType == null)
+ throw new ArgumentNullException(nameof(returnType));
+
+ return ReadAsync<object>(utf8Json, returnType, options, cancellationToken);
+ }
+
+ private static async ValueTask<TValue> ReadAsync<TValue>(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ ReadStack state = default;
+ JsonClassInfo classInfo = options.GetOrAddClass(returnType);
+ state.Current.JsonClassInfo = classInfo;
+ if (classInfo.ClassType != ClassType.Object)
+ {
+ state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty();
+ }
+
+ var readerState = new JsonReaderState(options: options.ReaderOptions);
+
+ int bytesRemaining = 0;
+ int bytesRead;
+
+ // todo: switch to ArrayBuffer implementation to handle and simplify the allocs?
+ byte[] buffer = ArrayPool<byte>.Shared.Rent(options.EffectiveBufferSize);
+ int bufferSize = buffer.Length;
+ int deserializeBufferSize;
+ bool isFinalBlock;
+
+ try
+ {
+ do
+ {
+ int bytesToRead = bufferSize - bytesRemaining;
+ bytesRead = await utf8Json.ReadAsync(buffer, bytesRemaining, bytesToRead, cancellationToken).ConfigureAwait(false);
+
+ deserializeBufferSize = bytesRemaining + bytesRead;
+ isFinalBlock = (bytesRead == 0);
+
+ ReadCore(
+ ref readerState,
+ isFinalBlock,
+ buffer,
+ deserializeBufferSize,
+ options,
+ ref state);
+
+ int bytesConsumed = (int)readerState.BytesConsumed;
+ bytesRemaining = deserializeBufferSize - bytesConsumed;
+
+ if (isFinalBlock)
+ {
+ break;
+ }
+
+ // Check if we need to shift or expand the buffer because there wasn't enough data to complete deserialization.
+ if (bytesConsumed <= (bufferSize / 2))
+ {
+ // We have less than half the buffer available, double the buffer size.
+ bufferSize = (bufferSize < HalfMaxValue) ? bufferSize * 2 : int.MaxValue;
+
+ byte[] dest = ArrayPool<byte>.Shared.Rent(bufferSize);
+ bufferSize = dest.Length;
+ if (bytesRemaining > 0)
+ {
+ // Copy the unprocessed data to the new buffer while shifting the processed bytes.
+ Buffer.BlockCopy(buffer, bytesConsumed, dest, 0, bytesRemaining);
+ }
+
+ ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
+ buffer = dest;
+ }
+ else if (bytesRemaining > 0)
+ {
+ // Shift the processed bytes to the beginning of buffer to make more room.
+ Buffer.BlockCopy(buffer, bytesConsumed, buffer, 0, bytesRemaining);
+ }
+ } while (true);
+ }
+ finally
+ {
+ ArrayPool<byte>.Shared.Return(buffer, clearArray: true);
+ }
+
+ if (bytesRemaining != 0)
+ {
+ throw new JsonReaderException(SR.Format(SR.DeserializeDataRemaining,
+ deserializeBufferSize, bytesRemaining), readerState);
+ }
+
+ return (TValue)state.Current.ReturnValue;
+ }
+
+ private static void ReadCore(
+ ref JsonReaderState readerState,
+ bool isFinalBlock,
+ byte[] buffer,
+ int bytesToRead,
+ JsonSerializerOptions options,
+ ref ReadStack state)
+ {
+ Utf8JsonReader reader = new Utf8JsonReader(buffer.AsSpan(0, bytesToRead), isFinalBlock, readerState);
+
+ ReadCore(
+ options,
+ ref reader,
+ ref state);
+
+ readerState = reader.CurrentState;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ public static TValue Parse<TValue>(string json, JsonSerializerOptions options = null)
+ {
+ if (json == null)
+ throw new ArgumentNullException(nameof(json));
+
+ return (TValue)ParseCore(json, typeof(TValue), options);
+ }
+
+ public static object Parse(string json, Type returnType, JsonSerializerOptions options = null)
+ {
+ if (json == null)
+ throw new ArgumentNullException(nameof(json));
+
+ if (returnType == null)
+ throw new ArgumentNullException(nameof(returnType));
+
+ return ParseCore(json, returnType, options);
+ }
+
+ private static object ParseCore(string json, Type returnType, JsonSerializerOptions options = null)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ // todo: use an array pool here for smaller requests to avoid the alloc. Also doc the API that UTF8 is preferred for perf.
+ byte[] jsonBytes = JsonReaderHelper.s_utf8Encoding.GetBytes(json);
+ var readerState = new JsonReaderState(options: options.ReaderOptions);
+ var reader = new Utf8JsonReader(jsonBytes, isFinalBlock: true, readerState);
+ object result = ReadCore(returnType, options, ref reader);
+
+ readerState = reader.CurrentState;
+ if (readerState.BytesConsumed != jsonBytes.Length)
+ {
+ throw new JsonReaderException(SR.Format(SR.DeserializeDataRemaining,
+ jsonBytes.Length, jsonBytes.Length - readerState.BytesConsumed), readerState);
+ }
+
+ return result;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ internal static readonly JsonPropertyInfo s_missingProperty = new JsonPropertyInfo<object, object>();
+ private static readonly JsonSerializerOptions s_defaultSettings = new JsonSerializerOptions();
+
+ private static object ReadCore(
+ Type returnType,
+ JsonSerializerOptions options,
+ ref Utf8JsonReader reader)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ ReadStack state = default;
+ JsonClassInfo classInfo = options.GetOrAddClass(returnType);
+ state.Current.JsonClassInfo = classInfo;
+ if (classInfo.ClassType != ClassType.Object)
+ {
+ state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty();
+ }
+
+ ReadCore(options, ref reader, ref state);
+
+ return state.Current.ReturnValue;
+ }
+
+ // todo: for readability, refactor this method to split by ClassType(Enumerable, Object, or Value) like Write()
+ private static void ReadCore(
+ JsonSerializerOptions options,
+ ref Utf8JsonReader reader,
+ ref ReadStack state)
+ {
+ while (reader.Read())
+ {
+ JsonTokenType tokenType = reader.TokenType;
+
+ if (JsonHelpers.IsInRangeInclusive(tokenType, JsonTokenType.String, JsonTokenType.False))
+ {
+ Debug.Assert(tokenType == JsonTokenType.String || tokenType == JsonTokenType.Number || tokenType == JsonTokenType.True || tokenType == JsonTokenType.False);
+
+ if (HandleValue(tokenType, options, ref reader, ref state))
+ {
+ return;
+ }
+ }
+ else if (tokenType == JsonTokenType.PropertyName)
+ {
+ if (!state.Current.Drain)
+ {
+ Debug.Assert(state.Current.ReturnValue != default);
+ Debug.Assert(state.Current.JsonClassInfo != default);
+
+ ReadOnlySpan<byte> propertyName = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
+ state.Current.JsonPropertyInfo = state.Current.JsonClassInfo.GetProperty(propertyName, state.Current.PropertyIndex);
+ if (state.Current.JsonPropertyInfo == null)
+ {
+ state.Current.JsonPropertyInfo = s_missingProperty;
+ }
+
+ state.Current.PropertyIndex++;
+ }
+ }
+ else if (tokenType == JsonTokenType.StartObject)
+ {
+ HandleStartObject(options, ref state);
+ }
+ else if (tokenType == JsonTokenType.EndObject)
+ {
+ if (HandleEndObject(options, ref state))
+ {
+ return;
+ }
+ }
+ else if (tokenType == JsonTokenType.StartArray)
+ {
+ HandleStartArray(options, ref reader, ref state);
+ }
+ else if (tokenType == JsonTokenType.EndArray)
+ {
+ if (HandleEndArray(options, ref state))
+ {
+ return;
+ }
+ }
+ else if (tokenType == JsonTokenType.Null)
+ {
+ if (HandleNull(ref reader, ref state, options))
+ {
+ return;
+ }
+ }
+ }
+
+ return;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ // Name \ feature is pending closure on API review
+ public static byte[] ToBytes<TValue>(TValue value, JsonSerializerOptions options = null)
+ {
+ return WriteCoreBytes(value, typeof(TValue), options);
+ }
+
+ // Name \ feature is pending closure on API review
+ public static byte[] ToBytes(object value, Type type, JsonSerializerOptions options = null)
+ {
+ VerifyValueAndType(value, type);
+ return WriteCoreBytes(value, type, options);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static bool WriteEnumerable(
+ JsonSerializerOptions options,
+ ref Utf8JsonWriter writer,
+ ref WriteStack state)
+ {
+ return HandleEnumerable(state.Current.JsonClassInfo.ElementClassInfo, options, ref writer, ref state);
+ }
+
+ private static bool HandleEnumerable(
+ JsonClassInfo elementClassInfo,
+ JsonSerializerOptions options,
+ ref Utf8JsonWriter writer,
+ ref WriteStack state)
+ {
+ Debug.Assert(state.Current.JsonPropertyInfo.ClassType == ClassType.Enumerable);
+
+ JsonPropertyInfo propertyInfo = state.Current.JsonPropertyInfo;
+
+ if (state.Current.Enumerator == null)
+ {
+ if (propertyInfo._name == null)
+ {
+ writer.WriteStartArray();
+ }
+ else
+ {
+ writer.WriteStartArray(propertyInfo._name);
+ }
+
+ IEnumerable enumerable = (IEnumerable)propertyInfo.GetValueAsObject(state.Current.CurrentValue, options);
+
+ if (enumerable != null)
+ {
+ state.Current.Enumerator = enumerable.GetEnumerator();
+ }
+ }
+
+ if (state.Current.Enumerator != null && state.Current.Enumerator.MoveNext())
+ {
+ if (elementClassInfo.ClassType == ClassType.Value)
+ {
+ elementClassInfo.GetPolicyProperty().WriteEnumerable(options, ref state.Current, ref writer);
+ }
+ else
+ {
+ // An object or another enumerator requires a new stack frame
+ JsonClassInfo nextClassInfo = propertyInfo.ElementClassInfo;
+ object nextValue = state.Current.Enumerator.Current;
+ state.Push(nextClassInfo, nextValue);
+ }
+
+ return false;
+ }
+
+ // We are done enumerating.
+ writer.WriteEndArray();
+
+ if (state.Current.PopStackOnEndArray)
+ {
+ state.Pop();
+ }
+ else
+ {
+ state.Current.EndArray();
+ }
+
+ return true;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static bool WriteObject(
+ JsonSerializerOptions options,
+ ref Utf8JsonWriter writer,
+ ref WriteStack state)
+ {
+ JsonClassInfo classInfo = state.Current.JsonClassInfo;
+
+ // Write the start.
+ if (!state.Current.StartObjectWritten)
+ {
+ if (state.Current.JsonPropertyInfo?._escapedName == null)
+ {
+ writer.WriteStartObject();
+ }
+ else
+ {
+ writer.WriteStartObject(state.Current.JsonPropertyInfo._escapedName);
+ }
+ state.Current.StartObjectWritten = true;
+ }
+
+ // Determine if we are done enumerating properties.
+ if (state.Current.PropertyIndex != classInfo.PropertyCount)
+ {
+ HandleObject(options, ref writer, ref state);
+ return false;
+ }
+
+ writer.WriteEndObject();
+
+ if (state.Current.PopStackOnEndObject)
+ {
+ state.Pop();
+ }
+ else
+ {
+ state.Current.EndObject();
+ }
+
+ return true;
+ }
+
+ private static bool HandleObject(
+ JsonSerializerOptions options,
+ ref Utf8JsonWriter writer,
+ ref WriteStack state)
+ {
+ Debug.Assert(state.Current.JsonClassInfo.ClassType == ClassType.Object);
+
+ JsonPropertyInfo propertyInfo = state.Current.JsonClassInfo.GetProperty(state.Current.PropertyIndex);
+ state.Current.JsonPropertyInfo = propertyInfo;
+
+ ClassType propertyClassType = propertyInfo.ClassType;
+ if (propertyClassType == ClassType.Value)
+ {
+ propertyInfo.Write(options, ref state.Current, ref writer);
+ state.Current.NextProperty();
+ return true;
+ }
+
+ // A property that returns an enumerator keeps the same stack frame.
+ if (propertyClassType == ClassType.Enumerable)
+ {
+ bool endOfEnumerable = HandleEnumerable(propertyInfo.ElementClassInfo, options, ref writer, ref state);
+ if (endOfEnumerable)
+ {
+ state.Current.NextProperty();
+ }
+
+ return endOfEnumerable;
+ }
+
+ // A property that returns an object requires a new stack frame.
+ object value = propertyInfo.GetValueAsObject(state.Current.CurrentValue, options);
+ if (value != null)
+ {
+ JsonPropertyInfo previousPropertyInfo = state.Current.JsonPropertyInfo;
+
+ state.Current.NextProperty();
+
+ JsonClassInfo nextClassInfo = options.GetOrAddClass(propertyInfo.PropertyType);
+ state.Push(nextClassInfo, value);
+
+ // Set the PropertyInfo so we can obtain the property name in order to write it.
+ state.Current.JsonPropertyInfo = previousPropertyInfo;
+ }
+ else if (!propertyInfo.IgnoreNullPropertyValueOnWrite(options))
+ {
+ writer.WriteNull(propertyInfo._escapedName);
+ }
+
+ return true;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool WriteValue(
+ JsonSerializerOptions options,
+ ref Utf8JsonWriter writer,
+ ref WriteStackFrame current)
+ {
+ Debug.Assert(current.JsonPropertyInfo.ClassType == ClassType.Value);
+
+ current.JsonPropertyInfo.Write(options, ref current, ref writer);
+ return true;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static void VerifyValueAndType(object value, Type type)
+ {
+ if (type == null)
+ {
+ if (value != null)
+ {
+ throw new ArgumentNullException(nameof(type));
+ }
+ }
+ else if (value != null)
+ {
+ if (!type.IsAssignableFrom(value.GetType()))
+ {
+ ThrowHelper.ThrowArgumentException_DeserializeWrongType(type, value);
+ }
+ }
+ }
+
+ private static void WriteNull(
+ ref JsonWriterState writerState,
+ IBufferWriter<byte> bufferWriter)
+ {
+ Utf8JsonWriter writer = new Utf8JsonWriter(bufferWriter, writerState);
+ writer.WriteNullValue();
+ writer.Flush(true);
+ }
+
+ private static byte[] WriteCoreBytes(object value, Type type, JsonSerializerOptions options)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ byte[] result;
+
+ using (var output = new ArrayBufferWriter<byte>(options.EffectiveBufferSize))
+ {
+ WriteCore(output, value, type, options);
+ result = output.WrittenMemory.ToArray();
+ }
+
+ return result;
+ }
+
+ private static string WriteCoreString(object value, Type type, JsonSerializerOptions options)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ string result;
+
+ using (var output = new ArrayBufferWriter<byte>(options.EffectiveBufferSize))
+ {
+ WriteCore(output, value, type, options);
+ result = JsonReaderHelper.TranscodeHelper(output.WrittenMemory.Span);
+ }
+
+ return result;
+ }
+
+ private static void WriteCore(ArrayBufferWriter<byte> output, object value, Type type, JsonSerializerOptions options)
+ {
+ var writerState = new JsonWriterState(options.WriterOptions);
+ var writer = new Utf8JsonWriter(output, writerState);
+
+ if (value == null)
+ {
+ writer.WriteNullValue();
+ }
+ else
+ {
+ if (type == null)
+ {
+ type = value.GetType();
+ }
+
+ WriteStack state = default;
+ JsonClassInfo classInfo = options.GetOrAddClass(type);
+ state.Current.JsonClassInfo = classInfo;
+ state.Current.CurrentValue = value;
+ if (classInfo.ClassType != ClassType.Object)
+ {
+ state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty();
+ }
+
+ Write(ref writer, -1, options, ref state);
+ }
+
+ writer.Flush(isFinalBlock: true);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ public static Task WriteAsync<TValue>(TValue value, Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
+ {
+ return WriteAsyncCore(value, typeof(TValue), utf8Json, options, cancellationToken);
+ }
+
+ public static Task WriteAsync(object value, Type type, Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default)
+ {
+ if (utf8Json == null)
+ throw new ArgumentNullException(nameof(utf8Json));
+
+ VerifyValueAndType(value, type);
+
+ return WriteAsyncCore(value, type, utf8Json, options, cancellationToken);
+ }
+
+ private static async Task WriteAsyncCore(object value, Type type, Stream utf8Json, JsonSerializerOptions options, CancellationToken cancellationToken)
+ {
+ if (options == null)
+ options = s_defaultSettings;
+
+ var writerState = new JsonWriterState(options.WriterOptions);
+
+ using (var bufferWriter = new ArrayBufferWriter<byte>(options.EffectiveBufferSize))
+ {
+ if (value == null)
+ {
+ WriteNull(ref writerState, bufferWriter);
+#if BUILDING_INBOX_LIBRARY
+ await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
+#else
+ // todo: stackalloc or pool here?
+ await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false);
+#endif
+ return;
+ }
+
+ if (type == null)
+ {
+ type = value.GetType();
+ }
+
+ JsonClassInfo classInfo = options.GetOrAddClass(type);
+ WriteStack state = default;
+ state.Current.JsonClassInfo = classInfo;
+ state.Current.CurrentValue = value;
+ if (classInfo.ClassType != ClassType.Object)
+ {
+ state.Current.JsonPropertyInfo = classInfo.GetPolicyProperty();
+ }
+
+ bool isFinalBlock;
+
+ int flushThreshold;
+ do
+ {
+ flushThreshold = (int)(bufferWriter.Capacity * .9); //todo: determine best value here
+
+ isFinalBlock = Write(ref writerState, bufferWriter, flushThreshold, options, ref state);
+#if BUILDING_INBOX_LIBRARY
+ await utf8Json.WriteAsync(bufferWriter.WrittenMemory, cancellationToken).ConfigureAwait(false);
+#else
+ // todo: use pool here to avod extra alloc?
+ await utf8Json.WriteAsync(bufferWriter.WrittenMemory.ToArray(), 0, bufferWriter.WrittenMemory.Length, cancellationToken).ConfigureAwait(false);
+#endif
+ bufferWriter.Clear();
+ } while (!isFinalBlock);
+ }
+
+ // todo: verify that we do want to call FlushAsync here (or above). It seems like leaving it to the caller would be best.
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ public static string ToString<TValue>(TValue value, JsonSerializerOptions options = null)
+ {
+ return ToStringInternal(value, typeof(TValue), options);
+ }
+
+ public static string ToString(object value, Type type, JsonSerializerOptions options = null)
+ {
+ VerifyValueAndType(value, type);
+
+ return ToStringInternal(value, type, options);
+ }
+
+ private static string ToStringInternal(object value, Type type, JsonSerializerOptions options)
+ {
+ return WriteCoreString(value, type, options);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Buffers;
+
+namespace System.Text.Json.Serialization
+{
+ public static partial class JsonSerializer
+ {
+ private static bool Write(
+ ref JsonWriterState writerState,
+ IBufferWriter<byte> bufferWriter,
+ int flushThreshold,
+ JsonSerializerOptions options,
+ ref WriteStack state)
+ {
+ Utf8JsonWriter writer = new Utf8JsonWriter(bufferWriter, writerState);
+
+ bool isFinalBlock = Write(
+ ref writer,
+ flushThreshold,
+ options,
+ ref state);
+
+ writer.Flush(isFinalBlock: isFinalBlock);
+ writerState = writer.GetCurrentState();
+
+ return isFinalBlock;
+ }
+
+ private static bool Write(
+ ref Utf8JsonWriter writer,
+ int flushThreshold,
+ JsonSerializerOptions options,
+ ref WriteStack state)
+ {
+ bool continueWriting = true;
+ bool finishedSerializing;
+ do
+ {
+ switch (state.Current.JsonClassInfo.ClassType)
+ {
+ case ClassType.Enumerable:
+ finishedSerializing = WriteEnumerable(options, ref writer, ref state);
+ break;
+ case ClassType.Object:
+ finishedSerializing = WriteObject(options, ref writer, ref state);
+ break;
+ default:
+ finishedSerializing = WriteValue(options, ref writer, ref state.Current);
+ break;
+ }
+
+ if (flushThreshold >= 0 && writer.BytesWritten > flushThreshold)
+ {
+ return false;
+ }
+
+ if (finishedSerializing && writer.CurrentDepth == 0)
+ {
+ continueWriting = false;
+ }
+ } while (continueWriting);
+
+ return true;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Concurrent;
+
+namespace System.Text.Json.Serialization
+{
+ public sealed class JsonSerializerOptions
+ {
+ internal const int BufferSizeUnspecified = -1;
+ internal const int BufferSizeDefault = 16 * 1024;
+
+ private ClassMaterializer _classMaterializerStrategy;
+ private int _defaultBufferSize = BufferSizeUnspecified;
+
+ private static readonly ConcurrentDictionary<Type, JsonClassInfo> s_classes = new ConcurrentDictionary<Type, JsonClassInfo>();
+
+ public JsonSerializerOptions() { }
+
+ internal JsonClassInfo GetOrAddClass(Type classType)
+ {
+ JsonClassInfo result;
+
+ if (!s_classes.TryGetValue(classType, out result))
+ {
+ result = s_classes.GetOrAdd(classType, new JsonClassInfo(classType, this));
+ }
+
+ return result;
+ }
+
+ public JsonReaderOptions ReaderOptions { get; set; }
+ public JsonWriterOptions WriterOptions { get; set; }
+
+ public int DefaultBufferSize
+ {
+ get
+ {
+ return _defaultBufferSize;
+ }
+ set
+ {
+ if (value == 0 || value < BufferSizeUnspecified)
+ {
+ throw new ArgumentException(SR.SerializationInvalidBufferSize);
+ }
+
+ _defaultBufferSize = value;
+
+ if (_defaultBufferSize == BufferSizeUnspecified)
+ {
+ EffectiveBufferSize = BufferSizeDefault;
+ }
+ else
+ {
+ EffectiveBufferSize = _defaultBufferSize;
+ }
+ }
+ }
+
+ public bool IgnoreNullPropertyValueOnWrite { get; set; }
+ public bool IgnoreNullPropertyValueOnRead { get; set; }
+
+ // Used internally for performance to avoid checking BufferSizeUnspecified.
+ internal int EffectiveBufferSize { get; private set; } = BufferSizeDefault;
+
+ internal ClassMaterializer ClassMaterializerStrategy
+ {
+ get
+ {
+ if (_classMaterializerStrategy == null)
+ {
+#if BUILDING_INBOX_LIBRARY
+ _classMaterializerStrategy = new ReflectionEmitMaterializer();
+#else
+ // todo: should we attempt to detect here, or at least have a #define like #SUPPORTS_IL_EMIT
+ _classMaterializerStrategy = new ReflectionMaterializer();
+#endif
+ }
+
+ return _classMaterializerStrategy;
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization.Policies
+{
+ internal abstract class JsonValueConverter<TValue>
+ {
+ public abstract bool TryRead(Type valueType, ref Utf8JsonReader reader, out TValue value);
+ public abstract void Write(TValue value, ref Utf8JsonWriter writer);
+ public abstract void Write(Span<byte> escapedPropertyName, TValue value, ref Utf8JsonWriter writer);
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ internal readonly struct PropertyRef
+ {
+ public PropertyRef(ulong key, JsonPropertyInfo info)
+ {
+ Key = key;
+ Info = info;
+ }
+
+ public readonly ulong Key;
+ public readonly JsonPropertyInfo Info;
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ internal struct ReadStack
+ {
+ // A fields is used instead of a property to avoid value semantics.
+ public ReadStackFrame Current;
+
+ private List<ReadStackFrame> _previous;
+ public int _index;
+
+ public void Push()
+ {
+ if (_previous == null)
+ {
+ _previous = new List<ReadStackFrame>();
+ }
+
+ if (_index == _previous.Count)
+ {
+ // Need to allocate a new array element.
+ _previous.Add(Current);
+ }
+ else
+ {
+ Debug.Assert(_index < _previous.Count);
+
+ // Use a previously allocated slot.
+ Current = _previous[_index];
+ }
+
+ Current.Reset();
+ _index++;
+ }
+
+ public void Pop()
+ {
+ Debug.Assert(_index > 0);
+ Current = _previous[--_index];
+ }
+
+ public bool IsLastFrame => _index == 0;
+
+ // Return a property path in the form of: [FullNameOfType].FirstProperty.SecondProperty.LastProperty
+ public string PropertyPath
+ {
+ get
+ {
+ StringBuilder path = new StringBuilder();
+
+ if (_previous == null || _index == 0)
+ {
+ path.Append($"[{Current.JsonClassInfo.Type.FullName}]");
+ }
+ else
+ {
+ path.Append($"[{_previous[0].JsonClassInfo.Type.FullName}]");
+
+ for (int i = 0; i < _index; i++)
+ {
+ path.Append(GetPropertyName(_previous[i]));
+ }
+ }
+
+ path.Append(GetPropertyName(Current));
+
+ return path.ToString();
+ }
+ }
+
+ private string GetPropertyName(in ReadStackFrame frame)
+ {
+ if (frame.JsonPropertyInfo != null && frame.JsonClassInfo.ClassType == ClassType.Object)
+ {
+ return $".{frame.JsonPropertyInfo.PropertyInfo.Name}";
+ }
+
+ return string.Empty;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ internal struct ReadStackFrame
+ {
+ // The object (POCO or IEnumerable) that is being populated
+ internal object ReturnValue;
+ internal JsonClassInfo JsonClassInfo;
+
+ // Current property values
+ internal JsonPropertyInfo JsonPropertyInfo;
+ internal bool PopStackOnEndArray;
+ internal bool EnumerableCreated;
+
+ // Support System.Array and other types that don't implement IList
+ internal List<object> TempEnumerableValues;
+
+ // For performance, we order the properties by the first usage and this index helps find the right slot quicker.
+ internal int PropertyIndex;
+ internal bool Drain;
+
+ internal void Reset()
+ {
+ ReturnValue = null;
+ JsonClassInfo = null;
+ PropertyIndex = 0;
+ Drain = false;
+ ResetProperty();
+ }
+
+ internal void ResetProperty()
+ {
+ JsonPropertyInfo = null;
+ PopStackOnEndArray = false;
+ EnumerableCreated = false;
+ TempEnumerableValues = null;
+ }
+
+ internal bool IsEnumerable()
+ {
+ return JsonClassInfo.ClassType == ClassType.Enumerable;
+ }
+
+ internal bool Skip()
+ {
+ return Drain || ReferenceEquals(JsonPropertyInfo, JsonSerializer.s_missingProperty);
+ }
+
+ internal bool IsPropertyEnumerable()
+ {
+ if (JsonPropertyInfo != null)
+ {
+ return JsonPropertyInfo.ClassType == ClassType.Enumerable;
+ }
+
+ return false;
+ }
+
+ public Type GetElementType()
+ {
+ if (IsPropertyEnumerable())
+ {
+ return JsonPropertyInfo.ElementClassInfo.Type;
+ }
+
+ if (IsEnumerable())
+ {
+ return JsonClassInfo.ElementClassInfo.Type;
+ }
+
+ return JsonPropertyInfo.PropertyType;
+ }
+
+ internal static object CreateEnumerableValue(ref Utf8JsonReader reader, ref ReadStack state, JsonSerializerOptions options)
+ {
+ // If the property has an EnumerableConverter, then we use tempEnumerableValues.
+ if (state.Current.JsonPropertyInfo.EnumerableConverter != null)
+ {
+ state.Current.TempEnumerableValues = new List<object>();
+ return null;
+ }
+
+ Type propType = state.Current.JsonPropertyInfo.PropertyType;
+ if (typeof(IList).IsAssignableFrom(propType))
+ {
+ // If IList, add the members as we create them.
+ JsonClassInfo collectionClassInfo = options.GetOrAddClass(propType);
+ IList collection = (IList)collectionClassInfo.CreateObject();
+ return collection;
+ }
+ else
+ {
+ ThrowHelper.ThrowJsonReaderException_DeserializeUnableToConvertValue(propType, reader, state);
+ return null;
+ }
+ }
+
+ internal static IEnumerable GetEnumerableValue(in ReadStackFrame current)
+ {
+ if (current.IsEnumerable())
+ {
+ if (current.ReturnValue != null)
+ {
+ return (IEnumerable)current.ReturnValue;
+ }
+ }
+
+ // IEnumerable properties are finished (values added inline) unless they are using tempEnumerableValues.
+ return current.TempEnumerableValues;
+ }
+
+ internal void SetReturnValue(object value, JsonSerializerOptions options)
+ {
+ Debug.Assert(ReturnValue == null);
+ ReturnValue = value;
+ }
+
+ internal static void SetReturnValue(object value, JsonSerializerOptions options, ref ReadStackFrame current, bool setPropertyDirectly = false)
+ {
+ if (current.IsEnumerable())
+ {
+ if (current.TempEnumerableValues != null)
+ {
+ current.TempEnumerableValues.Add(value);
+ }
+ else
+ {
+ ((IList)current.ReturnValue).Add(value);
+ }
+ }
+ else if (!setPropertyDirectly && current.IsPropertyEnumerable())
+ {
+ Debug.Assert(current.JsonPropertyInfo != null);
+ Debug.Assert(current.ReturnValue != null);
+ if (current.TempEnumerableValues != null)
+ {
+ current.TempEnumerableValues.Add(value);
+ }
+ else
+ {
+ ((IList)current.JsonPropertyInfo.GetValueAsObject(current.ReturnValue, options)).Add(value);
+ }
+ }
+ else
+ {
+ Debug.Assert(current.JsonPropertyInfo != null);
+ current.JsonPropertyInfo.SetValueAsObject(current.ReturnValue, value, options);
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+#if BUILDING_INBOX_LIBRARY
+using System.Diagnostics;
+using System.Reflection;
+using System.Reflection.Emit;
+
+namespace System.Text.Json.Serialization
+{
+ internal sealed class ReflectionEmitMaterializer : ClassMaterializer
+ {
+ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type)
+ {
+ Debug.Assert(type != null);
+
+ ConstructorInfo realMethod = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null);
+ if (realMethod == null)
+ {
+ return null;
+ }
+
+ var dynamicMethod = new DynamicMethod(
+ realMethod.Name,
+ type,
+ Type.EmptyTypes,
+ typeof(ReflectionEmitMaterializer).Module,
+ skipVisibility: true);
+
+ ILGenerator generator = dynamicMethod.GetILGenerator();
+ generator.Emit(OpCodes.Newobj, realMethod);
+ generator.Emit(OpCodes.Ret);
+
+ return (JsonClassInfo.ConstructorDelegate)dynamicMethod.CreateDelegate(typeof(JsonClassInfo.ConstructorDelegate));
+ }
+ }
+}
+#endif
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text.Json.Serialization
+{
+ internal sealed class ReflectionMaterializer : ClassMaterializer
+ {
+ public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type)
+ {
+ return () => Activator.CreateInstance(type);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace System.Text.Json.Serialization
+{
+ internal struct WriteStack
+ {
+ // Fields are used instead of properties to avoid value semantics.
+ public WriteStackFrame Current;
+ private List<WriteStackFrame> _previous;
+ private int _index;
+
+ public void Push()
+ {
+ if (_previous == null)
+ {
+ _previous = new List<WriteStackFrame>();
+ }
+
+ if (_index == _previous.Count)
+ {
+ // Need to allocate a new array element.
+ _previous.Add(Current);
+ }
+ else
+ {
+ Debug.Assert(_index < _previous.Count);
+
+ // Use a previously allocated slot.
+ Current = _previous[_index];
+ }
+
+ Current.Reset();
+ _index++;
+ }
+
+ public void Push(JsonClassInfo nextClassInfo, object nextValue)
+ {
+ Push();
+ Current.JsonClassInfo = nextClassInfo;
+ Current.CurrentValue = nextValue;
+
+ if (nextClassInfo.ClassType == ClassType.Enumerable)
+ {
+ Current.PopStackOnEndArray = true;
+ Current.JsonPropertyInfo = Current.JsonClassInfo.GetPolicyProperty();
+ }
+ else
+ {
+ Debug.Assert(nextClassInfo.ClassType == ClassType.Object);
+ Current.PopStackOnEndObject = true;
+ }
+ }
+
+ public void Pop()
+ {
+ Debug.Assert(_index > 0);
+ Current = _previous[--_index];
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections;
+
+namespace System.Text.Json.Serialization
+{
+ internal struct WriteStackFrame
+ {
+ // The object (POCO or IEnumerable) that is being populated
+ internal object CurrentValue;
+ internal JsonClassInfo JsonClassInfo;
+
+ internal IEnumerator Enumerator;
+
+ // Current property values
+ internal JsonPropertyInfo JsonPropertyInfo;
+
+ // The current property.
+ internal int PropertyIndex;
+
+ // Has the Start tag been written
+ internal bool StartObjectWritten;
+
+ internal bool PopStackOnEndArray;
+ internal bool PopStackOnEndObject;
+
+ internal void Reset()
+ {
+ CurrentValue = null;
+ JsonClassInfo = null;
+ StartObjectWritten = false;
+ EndObject();
+ EndArray();
+ }
+
+ internal void EndObject()
+ {
+ PropertyIndex = 0;
+ PopStackOnEndObject = false;
+ EndProperty();
+ }
+
+ internal void EndArray()
+ {
+ Enumerator = null;
+ PopStackOnEndArray = false;
+ EndProperty();
+ }
+
+ internal void EndProperty()
+ {
+ JsonPropertyInfo = null;
+ }
+
+ internal void NextProperty()
+ {
+ JsonPropertyInfo = null;
+ PropertyIndex++;
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Runtime.CompilerServices;
+using System.Text.Json.Serialization;
+
+namespace System.Text.Json
+{
+ internal static partial class ThrowHelper
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowArgumentException_DeserializeWrongType(Type type, object value)
+ {
+ throw new ArgumentException(SR.Format(SR.DeserializeWrongType, type.FullName, value.GetType().FullName));
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowJsonReaderException_DeserializeUnableToConvertValue(Type propertyType, in Utf8JsonReader reader, in ReadStack state)
+ {
+ throw new JsonReaderException(SR.Format(SR.DeserializeUnableToConvertValue, state.PropertyPath, propertyType), reader.CurrentState);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowJsonReaderException_DeserializeCannotBeNull(in Utf8JsonReader reader, in ReadStack state)
+ {
+ throw new JsonReaderException(SR.Format(SR.DeserializeCannotBeNull, state.PropertyPath), reader.CurrentState);
+ }
+
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ public static void ThrowObjectDisposedException(string name)
+ {
+ throw new ObjectDisposedException(name);
+ }
+ }
+}
namespace System.Text.Json
{
- internal static class ThrowHelper
+ internal static partial class ThrowHelper
{
public static ArgumentException GetArgumentException_MaxDepthMustBePositive()
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class ArrayTests
+ {
+ [Fact]
+ public static void ReadClassWithStringArray()
+ {
+ TestClassWithStringArray obj = JsonSerializer.Parse<TestClassWithStringArray>(TestClassWithStringArray.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithObjectList()
+ {
+ TestClassWithObjectList obj = JsonSerializer.Parse<TestClassWithObjectList>(TestClassWithObjectList.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithObjectArray()
+ {
+ TestClassWithObjectArray obj = JsonSerializer.Parse<TestClassWithObjectArray>(TestClassWithObjectArray.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void ReadClassWithGenericList()
+ {
+ TestClassWithGenericList obj = JsonSerializer.Parse<TestClassWithGenericList>(TestClassWithGenericList.s_data);
+ obj.Verify();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class ArrayTests
+ {
+ [Fact]
+ public static void WriteClassWithStringArray()
+ {
+ string json;
+
+ {
+ TestClassWithStringArray obj = new TestClassWithStringArray();
+ obj.Initialize();
+ obj.Verify();
+ json = JsonSerializer.ToString(obj);
+ }
+
+ {
+ TestClassWithStringArray obj = JsonSerializer.Parse<TestClassWithStringArray>(json);
+ obj.Verify();
+ }
+
+ {
+ TestClassWithStringArray obj = JsonSerializer.Parse<TestClassWithStringArray>(TestClassWithStringArray.s_data);
+ obj.Verify();
+ }
+ }
+
+ [Fact]
+ public static void WriteClassWithObjectArray()
+ {
+ string json;
+
+ {
+ TestClassWithGenericList obj = new TestClassWithGenericList();
+ obj.Initialize();
+ obj.Verify();
+ json = JsonSerializer.ToString(obj);
+ }
+
+ {
+ TestClassWithGenericList obj = JsonSerializer.Parse<TestClassWithGenericList>(json);
+ obj.Verify();
+ }
+
+ {
+ TestClassWithGenericList obj = JsonSerializer.Parse<TestClassWithGenericList>(TestClassWithGenericList.s_data);
+ obj.Verify();
+ }
+ }
+
+ [Fact]
+ public static void WriteClassWithGenericList()
+ {
+ string json;
+
+ {
+ TestClassWithObjectList obj = new TestClassWithObjectList();
+ obj.Initialize();
+ obj.Verify();
+ json = JsonSerializer.ToString(obj);
+ }
+
+ {
+ TestClassWithObjectList obj = JsonSerializer.Parse<TestClassWithObjectList>(json);
+ obj.Verify();
+ }
+
+ {
+ TestClassWithObjectList obj = JsonSerializer.Parse<TestClassWithObjectList>(TestClassWithObjectList.s_data);
+ obj.Verify();
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static class CyclicTests
+ {
+ [Fact]
+ public static void WriteCyclicFail()
+ {
+ TestClassWithCycle obj = new TestClassWithCycle();
+ obj.Initialize();
+
+ // We don't allow cycles; we throw InvalidOperation instead of an unrecoverable StackOverflow.
+ Assert.Throws<InvalidOperationException>(() => JsonSerializer.ToString(obj));
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static class EnumTests
+ {
+ private static readonly string s_jsonStringEnum =
+ @"{" +
+ @"""MyEnum"" : ""Two""" +
+ @"}";
+
+ private static readonly string s_jsonIntEnum =
+ @"{" +
+ @"""MyEnum"" : 2" +
+ @"}";
+
+ [Fact]
+ public static void EnumAsStringFail()
+ {
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<SimpleTestClass>(s_jsonStringEnum));
+ }
+
+ [Fact]
+ public static void EnumAsInt()
+ {
+ SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(s_jsonIntEnum);
+ Assert.Equal(SampleEnum.Two, obj.MyEnum);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class NullTests
+ {
+ [Fact]
+ public static void ClassWithNull()
+ {
+ TestClassWithNull obj = JsonSerializer.Parse<TestClassWithNull>(TestClassWithNull.s_json);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void DefaultReadValue()
+ {
+ TestClassWithNullButInitialized obj = JsonSerializer.Parse<TestClassWithNullButInitialized>(TestClassWithNullButInitialized.s_json);
+ Assert.Equal(null, obj.MyString);
+ Assert.Equal(null, obj.MyInt);
+ }
+
+ [Fact]
+ public static void OverrideReadOnOption()
+ {
+ var options = new JsonSerializerOptions();
+ options.IgnoreNullPropertyValueOnRead = true;
+
+ TestClassWithNullButInitialized obj = JsonSerializer.Parse<TestClassWithNullButInitialized>(TestClassWithNullButInitialized.s_json, options);
+ Assert.Equal("Hello", obj.MyString);
+ Assert.Equal(1, obj.MyInt);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class NullTests
+ {
+ [Fact]
+ public static void DefaultWriteOptions()
+ {
+ var input = new TestClassWithNull();
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal(@"{""MyString"":null}", json);
+ }
+
+ [Fact]
+ public static void OverrideWriteOnOption()
+ {
+ JsonSerializerOptions options = new JsonSerializerOptions();
+ options.IgnoreNullPropertyValueOnWrite = true;
+
+ var input = new TestClassWithNull();
+ string json = JsonSerializer.ToString(input, options);
+ Assert.Equal(@"{}", json);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static class PolymorphicTests
+ {
+ [Fact]
+ public static void Standard()
+ {
+ Customer customer = new Customer();
+ customer.Initialize();
+ customer.Verify();
+
+ string json = JsonSerializer.ToString(customer);
+ Customer deserializedCustomer = JsonSerializer.Parse<Customer>(json);
+ deserializedCustomer.Verify();
+ }
+
+ [Fact]
+ public static void StaticAnalysis()
+ {
+ Customer customer = new Customer();
+ customer.Initialize();
+ customer.Verify();
+
+ Person person = customer;
+
+ // Generic inference used <TValue> = <Person>
+ string json = JsonSerializer.ToString(person);
+
+ Customer deserializedCustomer = JsonSerializer.Parse<Customer>(json);
+
+ // We only serialized the Person base class, so the Customer fields should be default.
+ Assert.Equal(typeof(Customer), deserializedCustomer.GetType());
+ Assert.Equal(0, deserializedCustomer.CreditLimit);
+ ((Person)deserializedCustomer).VerifyNonVirtual();
+ }
+
+ [Fact]
+ public static void WriteStringWithRuntimeType()
+ {
+ Customer customer = new Customer();
+ customer.Initialize();
+ customer.Verify();
+
+ Person person = customer;
+
+ string json = JsonSerializer.ToString(person, person.GetType());
+
+ Customer deserializedCustomer = JsonSerializer.Parse<Customer>(json);
+
+ // We serialized the Customer
+ Assert.Equal(typeof(Customer), deserializedCustomer.GetType());
+ deserializedCustomer.Verify();
+ }
+
+ [Fact]
+ public static void StaticAnalysisWithRelationship()
+ {
+ UsaCustomer usaCustomer = new UsaCustomer();
+ usaCustomer.Initialize();
+ usaCustomer.Verify();
+
+ // Note: this could be typeof(UsaAddress) if we preserve objects created in the ctor. Currently we only preserve IEnumerables.
+ Assert.Equal(typeof(Address), usaCustomer.Address.GetType());
+
+ Customer customer = usaCustomer;
+
+ // Generic inference used <TValue> = <Customer>
+ string json = JsonSerializer.ToString(customer);
+
+ UsaCustomer deserializedCustomer = JsonSerializer.Parse<UsaCustomer>(json);
+
+ // We only serialized the Customer base class
+ Assert.Equal(typeof(UsaCustomer), deserializedCustomer.GetType());
+ Assert.Equal(typeof(Address), deserializedCustomer.Address.GetType());
+ ((Customer)deserializedCustomer).VerifyNonVirtual();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static class PropertyVisibilityTests
+ {
+ [Fact]
+ public static void NoSetter()
+ {
+ var obj = new ClassWithNoSetter();
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(@"{""MyString"":""DefaultValue""}", json);
+
+ ClassWithNoSetter objCopy = JsonSerializer.Parse<ClassWithNoSetter>(json);
+ Assert.Equal("DefaultValue", objCopy.MyString);
+ }
+
+ [Fact]
+ public static void NoGetter()
+ {
+ var objNoSetter = new ClassWithNoSetter();
+
+ string json = JsonSerializer.ToString(objNoSetter);
+ Assert.Equal(@"{""MyString"":""DefaultValue""}", json);
+
+ ClassWithNoGetter objNoGetter = JsonSerializer.Parse<ClassWithNoGetter>(json);
+ Assert.Equal("DefaultValue", objNoGetter.GetMyString());
+ }
+
+ [Fact]
+ public static void PrivateGetter()
+ {
+ var obj = new ClassWithPrivateSetterAndGetter();
+ obj.SetMyString("Hello");
+
+ string json = JsonSerializer.ToString(obj);
+ Assert.Equal(@"{}", json);
+ }
+
+ [Fact]
+ public static void PrivateSetter()
+ {
+ string json = @"{""MyString"":""Hello""}";
+
+ ClassWithPrivateSetterAndGetter objCopy = JsonSerializer.Parse<ClassWithPrivateSetterAndGetter>(json);
+ Assert.Null(objCopy.GetMyString());
+ }
+
+ // Todo: add tests with missing object property and missing collection property.
+
+ public class ClassWithNoSetter
+ {
+ public ClassWithNoSetter()
+ {
+ MyString = "DefaultValue";
+ }
+
+ public string MyString { get; }
+ }
+
+ public class ClassWithNoGetter
+ {
+ string _myString = "";
+
+ public string MyString
+ {
+ set
+ {
+ _myString = value;
+ }
+ }
+
+ public string GetMyString()
+ {
+ return _myString;
+ }
+ }
+
+ public class ClassWithPrivateSetterAndGetter
+ {
+ private string MyString { get; set; }
+
+ public string GetMyString()
+ {
+ return MyString;
+ }
+
+ public void SetMyString(string value)
+ {
+ MyString = value;
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static class SpanTests
+ {
+ [Fact]
+ public static void NullObjectInputFail()
+ {
+ Assert.Throws<ArgumentNullException>(() => JsonSerializer.Parse<string>((ReadOnlySpan<byte>)null));
+ }
+
+ [Theory]
+ [MemberData(nameof(ReadSuccessCases))]
+ public static void Read(Type classType, byte[] data)
+ {
+ object obj = JsonSerializer.Parse(data, classType);
+ Assert.IsAssignableFrom(typeof(ITestClass), obj);
+ ((ITestClass)obj).Verify();
+ }
+
+ [Fact]
+ public static void ReadGenericApi()
+ {
+ SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(SimpleTestClass.s_data);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void VerifyValueFail()
+ {
+ Assert.Throws<ArgumentNullException>(() => JsonSerializer.ToString(new object(), (Type)null));
+ }
+
+ [Fact]
+ public static void VerifyTypeFail()
+ {
+ Assert.Throws<ArgumentException>(() => JsonSerializer.ToString(1, typeof(string)));
+ }
+
+ [Fact]
+ public static void NullObjectOutput()
+ {
+ byte[] encodedNull = Encoding.UTF8.GetBytes(@"null");
+
+ {
+ byte[] output = JsonSerializer.ToBytes(null, null);
+ Assert.Equal(encodedNull, output);
+ }
+
+ {
+ byte[] output = JsonSerializer.ToBytes(null, typeof(NullTests));
+ Assert.Equal(encodedNull, output);
+ }
+ }
+
+ public static IEnumerable<object[]> ReadSuccessCases
+ {
+ get
+ {
+ return TestData.ReadSuccessCases;
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class StreamTests
+ {
+ [Fact]
+ public static async void NullObjectInputFalse()
+ {
+ await Assert.ThrowsAsync<ArgumentNullException>(async () => await JsonSerializer.ReadAsync<string>((Stream)null));
+ }
+
+ [Fact]
+ public static async Task ReadSimpleObjectAsync()
+ {
+ using (MemoryStream stream = new MemoryStream(SimpleTestClass.s_data))
+ {
+ JsonSerializerOptions options = new JsonSerializerOptions
+ {
+ DefaultBufferSize = 1
+ };
+
+ SimpleTestClass obj = await JsonSerializer.ReadAsync<SimpleTestClass>(stream, options);
+ obj.Verify();
+ }
+ }
+
+ [Fact]
+ public static async Task ReadPrimitivesAsync()
+ {
+ using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(@"1")))
+ {
+ JsonSerializerOptions options = new JsonSerializerOptions
+ {
+ DefaultBufferSize = 1
+ };
+
+ int i = await JsonSerializer.ReadAsync<int>(stream, options);
+ Assert.Equal(1, i);
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class StreamTests
+ {
+ [Fact]
+ public async static Task VerifyValueFail()
+ {
+ MemoryStream stream = new MemoryStream();
+ await Assert.ThrowsAsync<ArgumentNullException>(async () => await JsonSerializer.WriteAsync("", null, stream));
+ }
+
+ [Fact]
+ public async static Task VerifyTypeFail()
+ {
+ MemoryStream stream = new MemoryStream();
+ await Assert.ThrowsAsync<ArgumentException>(async () => await JsonSerializer.WriteAsync(1, typeof(string), stream));
+ }
+
+ [Fact]
+ public static async Task NullObjectValue()
+ {
+ MemoryStream stream = new MemoryStream();
+ await JsonSerializer.WriteAsync((object)null, stream);
+
+ stream.Seek(0, SeekOrigin.Begin);
+
+ byte[] readBuffer = new byte[4];
+ int bytesRead = stream.Read(readBuffer, 0, 4);
+
+ Assert.Equal(4, bytesRead);
+ string value = Encoding.UTF8.GetString(readBuffer);
+ Assert.Equal("null", value);
+ }
+
+ [Fact]
+ public static async Task RoundTripAsync()
+ {
+ byte[] buffer;
+
+ using (TestStream stream = new TestStream(1))
+ {
+ await WriteAsync(stream);
+
+ // Make a copy
+ buffer = stream.ToArray();
+ }
+
+ using (TestStream stream = new TestStream(buffer))
+ {
+ await ReadAsync(stream);
+ }
+ }
+
+ private static async Task WriteAsync(TestStream stream)
+ {
+ JsonSerializerOptions options = new JsonSerializerOptions
+ {
+ // Will likely default to 4K due to buffer pooling.
+ DefaultBufferSize = 1
+ };
+
+ {
+ LargeDataTestClass obj = new LargeDataTestClass();
+ obj.Initialize();
+ obj.Verify();
+
+ await JsonSerializer.WriteAsync(obj, stream, options: options);
+ }
+
+ // Must be changed if the test classes change:
+ Assert.Equal(551_368, stream.TestWriteBytesCount);
+
+ // We should have more than one write called due to the large byte count.
+ Assert.True(stream.TestWriteCount > 0);
+
+ // We don't auto-flush.
+ Assert.True(stream.TestFlushCount == 0);
+ }
+
+ private static async Task ReadAsync(TestStream stream)
+ {
+ JsonSerializerOptions options = new JsonSerializerOptions
+ {
+ // Will likely default to 4K due to buffer pooling.
+ DefaultBufferSize = 1
+ };
+
+ LargeDataTestClass obj = await JsonSerializer.ReadAsync<LargeDataTestClass>(stream, options);
+ // Must be changed if the test classes change; may be > since last read may not have filled buffer.
+ Assert.True(stream.TestRequestedReadBytesCount >= 551368);
+
+ // We should have more than one read called due to the large byte count.
+ Assert.True(stream.TestReadCount > 0);
+
+ // We don't auto-flush.
+ Assert.True(stream.TestFlushCount == 0);
+
+ obj.Verify();
+ }
+
+ [Fact]
+ public static async Task WritePrimitivesAsync()
+ {
+ MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(@"1"));
+ JsonSerializerOptions options = new JsonSerializerOptions
+ {
+ DefaultBufferSize = 1
+ };
+
+ int i = await JsonSerializer.ReadAsync<int>(stream, options);
+ Assert.Equal(1, i);
+ }
+ }
+
+ public class TestStream : MemoryStream
+ {
+ public int TestFlushCount { get; private set; }
+
+ public int TestWriteCount { get; private set; }
+ public int TestWriteBytesCount { get; private set; }
+
+ public int TestReadCount { get; private set; }
+ public int TestRequestedReadBytesCount { get; private set; }
+
+ public TestStream(int capacity) : base(capacity) { }
+
+ public TestStream(byte[] buffer) : base(buffer) { }
+
+ public override Task FlushAsync(CancellationToken cancellationToken)
+ {
+ TestFlushCount++;
+ return base.FlushAsync(cancellationToken);
+ }
+
+#if BUILDING_INBOX_LIBRARY
+ public override ValueTask WriteAsync(ReadOnlyMemory<byte> source, CancellationToken cancellationToken)
+ {
+ TestWriteCount++;
+ TestWriteBytesCount += source.Length;
+ return base.WriteAsync(source, cancellationToken);
+ }
+#else
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ TestWriteCount++;
+ TestWriteBytesCount += (count - offset);
+ return base.WriteAsync(buffer, offset, count, cancellationToken);
+ }
+#endif
+
+ public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+ {
+ TestReadCount++;
+ TestRequestedReadBytesCount += count;
+
+ return base.ReadAsync(buffer, offset, count, cancellationToken);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class StringTests
+ {
+ [Fact]
+ public static void NullObjectInputFail()
+ {
+
+ Assert.Throws<ArgumentNullException>(() => JsonSerializer.Parse<string>((string)null));
+ }
+
+ [Fact]
+ public static void NullLiteralObjectInput()
+ {
+ {
+ string obj = JsonSerializer.Parse<string>("null");
+ Assert.Null(obj);
+ }
+
+ {
+ string obj = JsonSerializer.Parse<string>(@"""null""");
+ Assert.Equal("null", obj);
+ }
+ }
+
+ [Fact]
+ public static void EmptyStringInput()
+ {
+ string obj = JsonSerializer.Parse<string>(@"""""");
+ Assert.Equal(string.Empty, obj);
+ }
+
+ [Fact]
+ public static void ReadSimpleClass()
+ {
+ SimpleTestClass obj = JsonSerializer.Parse<SimpleTestClass>(SimpleTestClass.s_json);
+ obj.Verify();
+ }
+
+ [Fact]
+ public static void EmptyClassWithRandomData()
+ {
+ JsonSerializer.Parse<EmptyClass>(SimpleTestClass.s_json);
+ JsonSerializer.Parse<EmptyClass>(SimpleTestClassWithNulls.s_json);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class StringTests
+ {
+ [Fact]
+ public static void VerifyValueFail()
+ {
+ Assert.Throws<ArgumentNullException>(() => JsonSerializer.ToString("", (Type)null));
+ }
+
+ [Fact]
+ public static void VerifyTypeFail()
+ {
+ Assert.Throws<ArgumentException>(() => JsonSerializer.ToString(1, typeof(string)));
+ }
+
+ [Fact]
+ public static void NullObjectOutput()
+ {
+ {
+ string output = JsonSerializer.ToString<string>(null);
+ Assert.Equal("null", output);
+ }
+
+ {
+ string output = JsonSerializer.ToString<string>(null, null);
+ Assert.Equal("null", output);
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(WriteSuccessCases))]
+ public static void Write(ITestClass testObj)
+ {
+ string json;
+
+ {
+ testObj.Initialize();
+ testObj.Verify();
+ json = JsonSerializer.ToString(testObj, testObj.GetType());
+ }
+
+ {
+ ITestClass obj = (ITestClass)JsonSerializer.Parse(json, testObj.GetType());
+ obj.Verify();
+ }
+ }
+
+ public static IEnumerable<object[]> WriteSuccessCases
+ {
+ get
+ {
+ return TestData.WriteSuccessCases;
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public class Person : ITestClass
+ {
+ public string Name { get; set; }
+ public Address Address { get; set; }
+
+ public Person()
+ {
+ Address = new Address();
+ }
+
+ public virtual void Initialize()
+ {
+ Name = "MyName";
+
+ Address = new Address();
+ Address.Initialize();
+ }
+
+ public virtual void Verify()
+ {
+ Assert.Equal("MyName", Name);
+ Address.Verify();
+ }
+
+ public void VerifyNonVirtual()
+ {
+ Assert.Equal("MyName", Name);
+ Address.VerifyNonVirtual();
+ }
+ }
+
+ public class Address : ITestClass
+ {
+ public string City { get; set; }
+
+ public virtual void Initialize()
+ {
+ City = "MyCity";
+ }
+
+ public virtual void Verify()
+ {
+ Assert.Equal("MyCity", City);
+ }
+
+ public void VerifyNonVirtual()
+ {
+ Assert.Equal("MyCity", City);
+ }
+ }
+
+ public class Customer : Person, ITestClass
+ {
+ public decimal CreditLimit { get; set; }
+
+ public override void Initialize()
+ {
+ CreditLimit = 500;
+ base.Initialize();
+ }
+
+ public override void Verify()
+ {
+ Assert.Equal(500, CreditLimit);
+ base.Verify();
+ }
+
+ new public void VerifyNonVirtual()
+ {
+ Assert.Equal(500, CreditLimit);
+ }
+ }
+
+ public class UsaCustomer : Customer, ITestClass
+ {
+ public UsaCustomer() : base()
+ {
+ Address = new UsaAddress();
+ }
+ }
+
+ public class UsaAddress : Address, ITestClass
+ {
+ public string State { get; set; }
+
+ public override void Initialize()
+ {
+ State = "MyState";
+ base.Initialize();
+ }
+
+ public override void Verify()
+ {
+ Assert.Equal("MyState", State);
+ base.Verify();
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public interface ITestClass
+ {
+ void Initialize();
+ void Verify();
+ }
+
+ public enum SampleEnum
+ {
+ One = 1,
+ Two = 2
+ }
+
+ public class SimpleTestClass : ITestClass
+ {
+ public short MyInt16 { get; set; }
+ public int MyInt32 { get; set; }
+ public long MyInt64 { get; set; }
+ public ushort MyUInt16 { get; set; }
+ public uint MyUInt32 { get; set; }
+ public ulong MyUInt64 { get; set; }
+ public byte MyByte { get; set; }
+ public sbyte MySByte { get; set; }
+ public char MyChar { get; set; }
+ public string MyString { get; set; }
+ public decimal MyDecimal { get; set; }
+ public bool MyBooleanTrue { get; set; }
+ public bool MyBooleanFalse { get; set; }
+ public float MySingle { get; set; }
+ public double MyDouble { get; set; }
+ public DateTime MyDateTime { get; set; }
+ public SampleEnum MyEnum { get; set; }
+ public short[] MyInt16Array{ get; set; }
+ public int[] MyInt32Array{ get; set; }
+ public long[] MyInt64Array{ get; set; }
+ public ushort[] MyUInt16Array{ get; set; }
+ public uint[] MyUInt32Array{ get; set; }
+ public ulong[] MyUInt64Array{ get; set; }
+ public byte[] MyByteArray{ get; set; }
+ public sbyte[] MySByteArray{ get; set; }
+ public char[] MyCharArray{ get; set; }
+ public string[] MyStringArray{ get; set; }
+ public decimal[] MyDecimalArray{ get; set; }
+ public bool[] MyBooleanTrueArray{ get; set; }
+ public bool[] MyBooleanFalseArray{ get; set; }
+ public float[] MySingleArray{ get; set; }
+ public double[] MyDoubleArray{ get; set; }
+ public DateTime[] MyDateTimeArray{ get; set; }
+ public SampleEnum[] MyEnumArray{ get; set; }
+
+ public static readonly string s_json =
+ @"{" +
+ @"""MyInt16"" : 1," +
+ @"""MyInt32"" : 2," +
+ @"""MyInt64"" : 3," +
+ @"""MyUInt16"" : 4," +
+ @"""MyUInt32"" : 5," +
+ @"""MyUInt64"" : 6," +
+ @"""MyByte"" : 7," +
+ @"""MySByte"" : 8," +
+ @"""MyChar"" : ""a""," +
+ @"""MyString"" : ""Hello""," +
+ @"""MyBooleanTrue"" : true," +
+ @"""MyBooleanFalse"" : false," +
+ @"""MySingle"" : 1.1," +
+ @"""MyDouble"" : 2.2," +
+ @"""MyDecimal"" : 3.3," +
+ @"""MyDateTime"" : ""2019-01-30T12:01:02.0000000Z""," +
+ @"""MyEnum"" : 2," + // int by default
+ @"""MyInt16Array"" : [1]," +
+ @"""MyInt32Array"" : [2]," +
+ @"""MyInt64Array"" : [3]," +
+ @"""MyUInt16Array"" : [4]," +
+ @"""MyUInt32Array"" : [5]," +
+ @"""MyUInt64Array"" : [6]," +
+ @"""MyByteArray"" : [7]," +
+ @"""MySByteArray"" : [8]," +
+ @"""MyCharArray"" : [""a""]," +
+ @"""MyStringArray"" : [""Hello""]," +
+ @"""MyBooleanTrueArray"" : [true]," +
+ @"""MyBooleanFalseArray"" : [false]," +
+ @"""MySingleArray"" : [1.1]," +
+ @"""MyDoubleArray"" : [2.2]," +
+ @"""MyDecimalArray"" : [3.3]," +
+ @"""MyDateTimeArray"" : [""2019-01-30T12:01:02.0000000Z""]," +
+ @"""MyEnumArray"" : [2]" + // int by default
+ @"}";
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+
+ public void Initialize()
+ {
+ MyInt16 = 1;
+ MyInt32 = 2;
+ MyInt64 = 3;
+ MyUInt16 = 4;
+ MyUInt32 = 5;
+ MyUInt64 = 6;
+ MyByte = 7;
+ MySByte = 8;
+ MyChar = 'a';
+ MyString = "Hello";
+ MyBooleanTrue = true;
+ MyBooleanFalse = false;
+ MySingle = 1.1f;
+ MyDouble = 2.2d;
+ MyDecimal = 3.3m;
+ MyDateTime = new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc);
+ MyEnum = SampleEnum.Two;
+
+ MyInt16Array = new short[] { 1 };
+ MyInt32Array = new int[] { 2 };
+ MyInt64Array = new long[] { 3 };
+ MyUInt16Array = new ushort[] { 4 };
+ MyUInt32Array = new uint[] { 5 };
+ MyUInt64Array = new ulong[] { 6 };
+ MyByteArray = new byte[] { 7 };
+ MySByteArray = new sbyte[] { 8 };
+ MyCharArray = new char[] { 'a' };
+ MyStringArray = new string[] { "Hello" };
+ MyBooleanTrueArray = new bool[] { true };
+ MyBooleanFalseArray = new bool[] { false };
+ MySingleArray = new float[] { 1.1f };
+ MyDoubleArray = new double[] { 2.2d };
+ MyDecimalArray = new decimal[] { 3.3m };
+ MyDateTimeArray = new DateTime[] { new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc) };
+ MyEnumArray = new SampleEnum[] { SampleEnum.Two };
+ }
+
+ public void Verify()
+ {
+ Assert.Equal((short)1, MyInt16);
+ Assert.Equal((int)2, MyInt32);
+ Assert.Equal((long)3, MyInt64);
+ Assert.Equal((ushort)4, MyUInt16);
+ Assert.Equal((uint)5, MyUInt32);
+ Assert.Equal((ulong)6, MyUInt64);
+ Assert.Equal((byte)7, MyByte);
+ Assert.Equal((sbyte)8, MySByte);
+ Assert.Equal('a', MyChar);
+ Assert.Equal("Hello", MyString);
+ Assert.Equal(3.3m, MyDecimal);
+ Assert.Equal(false, MyBooleanFalse);
+ Assert.Equal(true, MyBooleanTrue);
+ Assert.Equal(1.1f, MySingle);
+ Assert.Equal(2.2d, MyDouble);
+ Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTime);
+ Assert.Equal(SampleEnum.Two, MyEnum);
+
+ Assert.Equal((short)1, MyInt16Array[0]);
+ Assert.Equal((int)2, MyInt32Array[0]);
+ Assert.Equal((long)3, MyInt64Array[0]);
+ Assert.Equal((ushort)4, MyUInt16Array[0]);
+ Assert.Equal((uint)5, MyUInt32Array[0]);
+ Assert.Equal((ulong)6, MyUInt64Array[0]);
+ Assert.Equal((byte)7, MyByteArray[0]);
+ Assert.Equal((sbyte)8, MySByteArray[0]);
+ Assert.Equal('a', MyCharArray[0]);
+ Assert.Equal("Hello", MyStringArray[0]);
+ Assert.Equal(3.3m, MyDecimalArray[0]);
+ Assert.Equal(false, MyBooleanFalseArray[0]);
+ Assert.Equal(true, MyBooleanTrueArray[0]);
+ Assert.Equal(1.1f, MySingleArray[0]);
+ Assert.Equal(2.2d, MyDoubleArray[0]);
+ Assert.Equal(new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc), MyDateTimeArray[0]);
+ Assert.Equal(SampleEnum.Two, MyEnumArray[0]);
+ }
+ }
+
+ public abstract class SimpleBaseClassWithNullables
+ {
+ public short? MyInt16 { get; set; }
+ public int? MyInt32 { get; set; }
+ public long? MyInt64 { get; set; }
+ public ushort? MyUInt16 { get; set; }
+ public uint? MyUInt32 { get; set; }
+ public ulong? MyUInt64 { get; set; }
+ public byte? MyByte { get; set; }
+ public sbyte? MySByte { get; set; }
+ public char? MyChar { get; set; }
+ public decimal? MyDecimal { get; set; }
+ public bool? MyBooleanTrue { get; set; }
+ public bool? MyBooleanFalse { get; set; }
+ public float? MySingle { get; set; }
+ public double? MyDouble { get; set; }
+ public DateTime? MyDateTime { get; set; }
+ public SampleEnum? MyEnum { get; set; }
+ }
+
+ public class SimpleTestClassWithNulls : SimpleBaseClassWithNullables, ITestClass
+ {
+ public void Initialize()
+ {
+ }
+
+ public void Verify()
+ {
+ Assert.Null(MyInt16);
+ Assert.Null(MyInt32);
+ Assert.Null(MyInt64);
+ Assert.Null(MyUInt16);
+ Assert.Null(MyUInt32);
+ Assert.Null(MyUInt64);
+ Assert.Null(MyByte);
+ Assert.Null(MySByte);
+ Assert.Null(MyChar);
+ Assert.Null(MyDecimal);
+ Assert.Null(MyBooleanFalse);
+ Assert.Null(MyBooleanTrue);
+ Assert.Null(MySingle);
+ Assert.Null(MyDouble);
+ Assert.Null(MyDateTime);
+ Assert.Null(MyEnum);
+ }
+ public static readonly string s_json =
+ @"{" +
+ @"""MyInt16"" : null," +
+ @"""MyInt32"" : null," +
+ @"""MyInt64"" : null," +
+ @"""MyUInt16"" : null," +
+ @"""MyUInt32"" : null," +
+ @"""MyUInt64"" : null," +
+ @"""MyByte"" : null," +
+ @"""MySByte"" : null," +
+ @"""MyChar"" : null," +
+ @"""MyBooleanTrue"" : null," +
+ @"""MyBooleanFalse"" : null," +
+ @"""MySingle"" : null," +
+ @"""MyDouble"" : null," +
+ @"""MyDecimal"" : null," +
+ @"""MyDateTime"" : null," +
+ @"""MyEnum"" : null" +
+ @"}";
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+ }
+
+ public class SimpleTestClassWithNullables : SimpleBaseClassWithNullables, ITestClass
+ {
+ public static readonly string s_json =
+ @"{" +
+ @"""MyInt16"" : 1," +
+ @"""MyInt32"" : 2," +
+ @"""MyInt64"" : 3," +
+ @"""MyUInt16"" : 4," +
+ @"""MyUInt32"" : 5," +
+ @"""MyUInt64"" : 6," +
+ @"""MyByte"" : 7," +
+ @"""MySByte"" : 8," +
+ @"""MyChar"" : ""a""," +
+ @"""MyBooleanTrue"" : true," +
+ @"""MyBooleanFalse"" : false," +
+ @"""MySingle"" : 1.1," +
+ @"""MyDouble"" : 2.2," +
+ @"""MyDecimal"" : 3.3," +
+ @"""MyDateTime"" : ""2019-01-30T12:01:02.0000000Z""," +
+ @"""MyEnum"" : 2" + // int by default
+ @"}";
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+
+ public void Initialize()
+ {
+ MyInt16 = 1;
+ MyInt32 = 2;
+ MyInt64 = 3;
+ MyUInt16 = 4;
+ MyUInt32 = 5;
+ MyUInt64 = 6;
+ MyByte = 7;
+ MySByte = 8;
+ MyChar = 'a';
+ MyBooleanTrue = true;
+ MyBooleanFalse = false;
+ MySingle = 1.1f;
+ MyDouble = 2.2d;
+ MyDecimal = 3.3m;
+ MyDateTime = new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc);
+ MyEnum = SampleEnum.Two;
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(MyInt16, (short)1);
+ Assert.Equal(MyInt32, (int)2);
+ Assert.Equal(MyInt64, (long)3);
+ Assert.Equal(MyUInt16, (ushort)4);
+ Assert.Equal(MyUInt32, (uint)5);
+ Assert.Equal(MyUInt64, (ulong)6);
+ Assert.Equal(MyByte, (byte)7);
+ Assert.Equal(MySByte, (sbyte)8);
+ Assert.Equal(MyChar, 'a');
+ Assert.Equal(MyDecimal, 3.3m);
+ Assert.Equal(MyBooleanFalse, false);
+ Assert.Equal(MyBooleanTrue, true);
+ Assert.Equal(MySingle, 1.1f);
+ Assert.Equal(MyDouble, 2.2d);
+ Assert.Equal(MyDateTime, new DateTime(2019, 1, 30, 12, 1, 2, DateTimeKind.Utc));
+ Assert.Equal(MyEnum, SampleEnum.Two);
+ }
+ }
+
+ public class TestClassWithNull
+ {
+ public string MyString { get; set; }
+ public static readonly string s_json =
+ @"{" +
+ @"""MyString"" : null" +
+ @"}";
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+
+ public void Initialize()
+ {
+ MyString = null;
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(MyString, null);
+ }
+ }
+
+ public class TestClassWithNullButInitialized
+ {
+ public string MyString { get; set; } = "Hello";
+ public int? MyInt { get; set; } = 1;
+ public static readonly string s_json =
+ @"{" +
+ @"""MyString"" : null," +
+ @"""MyInt"" : null" +
+ @"}";
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+ }
+
+ public class TestClassWithNestedObjectInner : ITestClass
+ {
+ public SimpleTestClass MyData { get; set; }
+
+ public static readonly string s_json =
+ @"{" +
+ @"""MyData"":" + SimpleTestClass.s_json +
+ @"}";
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(s_json);
+
+ public void Initialize()
+ {
+ MyData = new SimpleTestClass();
+ MyData.Initialize();
+ }
+
+ public void Verify()
+ {
+ Assert.NotNull(MyData);
+ MyData.Verify();
+ }
+ }
+
+ public class TestClassWithNestedObjectOuter : ITestClass
+ {
+ public TestClassWithNestedObjectInner MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":" + TestClassWithNestedObjectInner.s_json +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new TestClassWithNestedObjectInner();
+ MyData.Initialize();
+ }
+
+ public void Verify()
+ {
+ Assert.NotNull(MyData);
+ MyData.Verify();
+ }
+ }
+
+ public class TestClassWithObjectList : ITestClass
+ {
+ public List<SimpleTestClass> MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ SimpleTestClass.s_json + "," +
+ SimpleTestClass.s_json +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new List<SimpleTestClass>();
+
+ {
+ SimpleTestClass obj = new SimpleTestClass();
+ obj.Initialize();
+ MyData.Add(obj);
+ }
+
+ {
+ SimpleTestClass obj = new SimpleTestClass();
+ obj.Initialize();
+ MyData.Add(obj);
+ }
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(2, MyData.Count);
+ MyData[0].Verify();
+ MyData[1].Verify();
+ }
+ }
+
+ public class TestClassWithObjectArray : ITestClass
+ {
+ public SimpleTestClass[] MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ SimpleTestClass.s_json + "," +
+ SimpleTestClass.s_json +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ SimpleTestClass obj1 = new SimpleTestClass();
+ obj1.Initialize();
+
+ SimpleTestClass obj2 = new SimpleTestClass();
+ obj2.Initialize();
+
+ MyData = new SimpleTestClass[2] { obj1, obj2 };
+ }
+
+ public void Verify()
+ {
+ MyData[0].Verify();
+ MyData[1].Verify();
+ Assert.Equal(2, MyData.Length);
+ }
+ }
+
+ public class TestClassWithStringArray : ITestClass
+ {
+ public string[] MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ @"""Hello""," +
+ @"""World""" +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new string[] { "Hello", "World" };
+ }
+
+ public void Verify()
+ {
+ Assert.Equal("Hello", MyData[0]);
+ Assert.Equal("World", MyData[1]);
+ Assert.Equal(2, MyData.Length);
+ }
+ }
+
+ public class TestClassWithCycle
+ {
+ public TestClassWithCycle Parent { get; set; }
+
+ public void Initialize()
+ {
+ Parent = this;
+ }
+ }
+
+ public class TestClassWithGenericList : ITestClass
+ {
+ public List<string> MyData { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""MyData"":[" +
+ @"""Hello""," +
+ @"""World""" +
+ @"]" +
+ @"}");
+
+ public void Initialize()
+ {
+ MyData = new List<string>
+ {
+ "Hello",
+ "World"
+ };
+ Assert.Equal(2, MyData.Count);
+ }
+
+ public void Verify()
+ {
+ Assert.Equal("Hello", MyData[0]);
+ Assert.Equal("World", MyData[1]);
+ Assert.Equal(2, MyData.Count);
+ }
+ }
+
+ public class SimpleDerivedTestClass : SimpleTestClass
+ {
+ }
+
+ public class OverridePropertyNameRuntime_TestClass
+ {
+ public Int16 MyInt16 { get; set; }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""blah"" : 1" +
+ @"}"
+ );
+ }
+
+ public class LargeDataTestClass : ITestClass
+ {
+ public List<LargeDataChildTestClass> Children { get; set; } = new List<LargeDataChildTestClass>();
+ public const int ChildrenCount = 10;
+
+ public string MyString { get; set; }
+ public const int MyStringLength = 1000;
+
+ public void Initialize()
+ {
+ MyString = new string('1', MyStringLength);
+
+ for (int i = 0; i < ChildrenCount; i++)
+ {
+ var child = new LargeDataChildTestClass
+ {
+ MyString = new string('2', LargeDataChildTestClass.MyStringLength),
+ MyStringArray = new string[LargeDataChildTestClass.MyStringArrayArrayCount]
+ };
+ for (int j = 0; j < child.MyStringArray.Length; j++)
+ {
+ child.MyStringArray[j] = new string('3', LargeDataChildTestClass.MyStringArrayElementStringLength);
+ }
+
+ Children.Add(child);
+ }
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(MyStringLength, MyString.Length);
+ Assert.Equal('1', MyString[0]);
+ Assert.Equal('1', MyString[MyStringLength - 1]);
+
+ Assert.Equal(ChildrenCount, Children.Count);
+ for (int i = 0; i < ChildrenCount; i++)
+ {
+ LargeDataChildTestClass child = Children[i];
+ Assert.Equal(LargeDataChildTestClass.MyStringLength, child.MyString.Length);
+ Assert.Equal('2', child.MyString[0]);
+ Assert.Equal('2', child.MyString[LargeDataChildTestClass.MyStringLength - 1]);
+
+ Assert.Equal(LargeDataChildTestClass.MyStringArrayArrayCount, child.MyStringArray.Length);
+ for (int j = 0; j < LargeDataChildTestClass.MyStringArrayArrayCount; j++)
+ {
+ Assert.Equal('3', child.MyStringArray[j][0]);
+ Assert.Equal('3', child.MyStringArray[j][LargeDataChildTestClass.MyStringArrayElementStringLength - 1]);
+ }
+ }
+ }
+ }
+
+ public class LargeDataChildTestClass
+ {
+ public const int MyStringLength = 2000;
+ public string MyString { get; set; }
+
+ public string[] MyStringArray { get; set; }
+ public const int MyStringArrayArrayCount = 1000;
+ public const int MyStringArrayElementStringLength = 50;
+ }
+
+ public class EmptyClass { }
+
+ public class BasicJson : ITestClass
+ {
+ public int age { get; set; }
+ public string first { get; set; }
+ public string last { get; set; }
+ public List<string> phoneNumbers { get; set; }
+ public BasicJsonAddress address { get; set; }
+
+ public void Initialize()
+ {
+ age = 30;
+ first = "John";
+ last = "Smith";
+ phoneNumbers = new List<string> { "425-000-0000", "425-000-0001" };
+ address = new BasicJsonAddress
+ {
+ street = "1 Microsoft Way",
+ city = "Redmond",
+ zip = 98052
+ };
+ }
+
+ public void Verify()
+ {
+ Assert.Equal(30, age);
+ Assert.Equal("John", first);
+ Assert.Equal("Smith", last);
+ Assert.Equal("425-000-0000", phoneNumbers[0]);
+ Assert.Equal("425-000-0001", phoneNumbers[1]);
+ Assert.Equal("1 Microsoft Way", address.street);
+ Assert.Equal("Redmond", address.city);
+ Assert.Equal(98052, address.zip);
+ }
+
+ public static readonly byte[] s_data = Encoding.UTF8.GetBytes(
+ "{" +
+ @"""age"" : 30," +
+ @"""first"" : ""John""," +
+ @"""last"" : ""Smith""," +
+ @"""phoneNumbers"" : [" +
+ @"""425-000-0000""," +
+ @"""425-000-0001""" +
+ @"]," +
+ @"""address"" : {" +
+ @"""street"" : ""1 Microsoft Way""," +
+ @"""city"" : ""Redmond""," +
+ @"""zip"" : 98052" +
+ "}" +
+ "}");
+ }
+
+ public class BasicJsonAddress
+ {
+ public string street { get; set; }
+ public string city { get; set; }
+ public int zip { get; set; }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static class TestData
+ {
+ public static IEnumerable<object[]> ReadSuccessCases
+ {
+ get
+ {
+ yield return new object[] { typeof(SimpleTestClass), SimpleTestClass.s_data };
+ yield return new object[] { typeof(SimpleTestClassWithNullables), SimpleTestClassWithNullables.s_data };
+ yield return new object[] { typeof(SimpleTestClassWithNulls), SimpleTestClassWithNulls.s_data };
+ yield return new object[] { typeof(BasicJson), BasicJson.s_data };
+ yield return new object[] { typeof(TestClassWithNestedObjectInner), TestClassWithNestedObjectInner.s_data };
+ yield return new object[] { typeof(TestClassWithNestedObjectOuter), TestClassWithNestedObjectOuter.s_data };
+ yield return new object[] { typeof(TestClassWithObjectArray), TestClassWithObjectArray.s_data };
+ yield return new object[] { typeof(TestClassWithStringArray), TestClassWithStringArray.s_data };
+ yield return new object[] { typeof(TestClassWithGenericList), TestClassWithGenericList.s_data };
+ }
+ }
+ public static IEnumerable<object[]> WriteSuccessCases
+ {
+ get
+ {
+ yield return new object[] { new SimpleTestClass() };
+ yield return new object[] { new SimpleTestClassWithNullables() };
+ yield return new object[] { new SimpleTestClassWithNulls() };
+ yield return new object[] { new BasicJson() };
+ yield return new object[] { new TestClassWithNestedObjectInner() };
+ yield return new object[] { new TestClassWithNestedObjectOuter() };
+ yield return new object[] { new TestClassWithObjectArray() };
+ yield return new object[] { new TestClassWithStringArray() };
+ yield return new object[] { new TestClassWithGenericList() };
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class ValueTests
+ {
+ [Fact]
+ public static void ReadPrimitives()
+ {
+ int i = JsonSerializer.Parse<int>(Encoding.UTF8.GetBytes(@"1"));
+ Assert.Equal(1, i);
+
+ int i2 = JsonSerializer.Parse<int>("2");
+ Assert.Equal(2, i2);
+
+ long l = JsonSerializer.Parse<long>(Encoding.UTF8.GetBytes(long.MaxValue.ToString()));
+ Assert.Equal(long.MaxValue, l);
+
+ long l2 = JsonSerializer.Parse<long>(long.MaxValue.ToString());
+ Assert.Equal(long.MaxValue, l2);
+
+ string s = JsonSerializer.Parse<string>(Encoding.UTF8.GetBytes(@"""Hello"""));
+ Assert.Equal("Hello", s);
+
+ string s2 = JsonSerializer.Parse<string>(@"""Hello""");
+ Assert.Equal("Hello", s2);
+ }
+
+ [Fact]
+ public static void ReadPrimitivesFail()
+ {
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int>(Encoding.UTF8.GetBytes(@"a")));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int[]>(Encoding.UTF8.GetBytes(@"[1,a]")));
+ }
+
+ [Fact]
+ public static void ReadPrimitiveArray()
+ {
+ int[] i = JsonSerializer.Parse<int[]>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ Assert.Equal(1, i[0]);
+ Assert.Equal(2, i[1]);
+ }
+
+ [Fact]
+ public static void ReadArrayWithEnums()
+ {
+ SampleEnum[] i = JsonSerializer.Parse<SampleEnum[]>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ Assert.Equal(SampleEnum.One, i[0]);
+ Assert.Equal(SampleEnum.Two, i[1]);
+ }
+
+ [Fact]
+ public static void ReadPrimitiveArrayFail()
+ {
+ // Invalid data
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int[]>(Encoding.UTF8.GetBytes(@"[1,""a""]")));
+
+ // Multidimensional arrays currently not supported
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int[,]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]")));
+ }
+
+ [Fact]
+ public static void ReadPrimitiveExtraBytesFail()
+ {
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int[]>("[2] {3}"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int[]>(Encoding.UTF8.GetBytes(@"[2] {3}")));
+ }
+
+ [Fact]
+ public static void RangeFail()
+ {
+ // These have custom code because the reader doesn't natively support:
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte>((byte.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte>((byte.MaxValue + 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte?>((byte.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte?>((byte.MaxValue + 1).ToString()));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte>((sbyte.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte>((sbyte.MaxValue + 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte?>((sbyte.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte?>((sbyte.MaxValue + 1).ToString()));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short>((short.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short>((short.MaxValue + 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short?>((short.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short?>((short.MaxValue + 1).ToString()));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort>((ushort.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort>((ushort.MaxValue + 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort?>((ushort.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort?>((ushort.MaxValue + 1).ToString()));
+
+ // To ensure range failure, just use double's MinValue and MaxValue (instead of float.MinValue\MaxValue +-1)
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float>(double.MinValue.ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float>(double.MaxValue.ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float?>(double.MinValue.ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float?>(double.MaxValue.ToString()));
+
+ // These are natively supported by the reader:
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int>(((long)int.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int>(((long)int.MaxValue + 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int?>(((long)int.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int?>(((long)int.MaxValue + 1).ToString()));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint>(((long)uint.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint>(((long)uint.MaxValue + 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint?>(((long)uint.MinValue - 1).ToString()));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint?>(((long)uint.MaxValue + 1).ToString()));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long>(long.MinValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long>(long.MaxValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long?>(long.MinValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long?>(long.MaxValue.ToString() + "0"));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong>(ulong.MinValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong>(ulong.MaxValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong?>(ulong.MinValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong?>(ulong.MaxValue.ToString() + "0"));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal>(decimal.MinValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal>(decimal.MaxValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal?>(decimal.MinValue.ToString() + "0"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal?>(decimal.MaxValue.ToString() + "0"));
+
+ // todo: determine why these don't throw (issue with reader?)
+ //Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double>(double.MinValue.ToString() + "0"));
+ //Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double>(double.MaxValue.ToString() + "0"));
+ //Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double?>(double.MinValue.ToString() + "0"));
+ //Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double?>(double.MaxValue.ToString() + "0"));
+ }
+
+ [Fact]
+ public static void RangePass()
+ {
+ Assert.Equal(byte.MinValue, JsonSerializer.Parse<byte>(byte.MinValue.ToString()));
+ Assert.Equal(byte.MaxValue, JsonSerializer.Parse<byte>(byte.MaxValue.ToString()));
+ Assert.Equal(byte.MinValue, JsonSerializer.Parse<byte?>(byte.MinValue.ToString()));
+ Assert.Equal(byte.MaxValue, JsonSerializer.Parse<byte?>(byte.MaxValue.ToString()));
+
+ Assert.Equal(sbyte.MinValue, JsonSerializer.Parse<sbyte>(sbyte.MinValue.ToString()));
+ Assert.Equal(sbyte.MaxValue, JsonSerializer.Parse<sbyte>(sbyte.MaxValue.ToString()));
+ Assert.Equal(sbyte.MinValue, JsonSerializer.Parse<sbyte?>(sbyte.MinValue.ToString()));
+ Assert.Equal(sbyte.MaxValue, JsonSerializer.Parse<sbyte?>(sbyte.MaxValue.ToString()));
+
+ Assert.Equal(short.MinValue, JsonSerializer.Parse<short>(short.MinValue.ToString()));
+ Assert.Equal(short.MaxValue, JsonSerializer.Parse<short>(short.MaxValue.ToString()));
+ Assert.Equal(short.MinValue, JsonSerializer.Parse<short?>(short.MinValue.ToString()));
+ Assert.Equal(short.MaxValue, JsonSerializer.Parse<short?>(short.MaxValue.ToString()));
+
+ Assert.Equal(ushort.MinValue, JsonSerializer.Parse<ushort>(ushort.MinValue.ToString()));
+ Assert.Equal(ushort.MaxValue, JsonSerializer.Parse<ushort>(ushort.MaxValue.ToString()));
+ Assert.Equal(ushort.MinValue, JsonSerializer.Parse<ushort?>(ushort.MinValue.ToString()));
+ Assert.Equal(ushort.MaxValue, JsonSerializer.Parse<ushort?>(ushort.MaxValue.ToString()));
+
+ // todo: these fail due to double->float conversion
+ //Assert.Equal(float.MinValue, JsonSerializer.Parse<float>(float.MinValue.ToString()));
+ //Assert.Equal(float.MaxValue, JsonSerializer.Parse<float>(float.MaxValue.ToString()));
+ //Assert.Equal(float.MinValue, JsonSerializer.Parse<float?>(float.MinValue.ToString()));
+ //Assert.Equal(float.MaxValue, JsonSerializer.Parse<float?>(float.MaxValue.ToString()));
+
+ Assert.Equal(int.MinValue, JsonSerializer.Parse<int>(int.MinValue.ToString()));
+ Assert.Equal(int.MaxValue, JsonSerializer.Parse<int>(int.MaxValue.ToString()));
+ Assert.Equal(int.MinValue, JsonSerializer.Parse<int?>(int.MinValue.ToString()));
+ Assert.Equal(int.MaxValue, JsonSerializer.Parse<int?>(int.MaxValue.ToString()));
+
+ Assert.Equal(uint.MinValue, JsonSerializer.Parse<uint>(uint.MinValue.ToString()));
+ Assert.Equal(uint.MaxValue, JsonSerializer.Parse<uint>(uint.MaxValue.ToString()));
+ Assert.Equal(uint.MinValue, JsonSerializer.Parse<uint?>(uint.MinValue.ToString()));
+ Assert.Equal(uint.MaxValue, JsonSerializer.Parse<uint?>(uint.MaxValue.ToString()));
+
+ Assert.Equal(long.MinValue, JsonSerializer.Parse<long>(long.MinValue.ToString()));
+ Assert.Equal(long.MaxValue, JsonSerializer.Parse<long>(long.MaxValue.ToString()));
+ Assert.Equal(long.MinValue, JsonSerializer.Parse<long?>(long.MinValue.ToString()));
+ Assert.Equal(long.MaxValue, JsonSerializer.Parse<long?>(long.MaxValue.ToString()));
+
+ Assert.Equal(ulong.MinValue, JsonSerializer.Parse<ulong>(ulong.MinValue.ToString()));
+ Assert.Equal(ulong.MaxValue, JsonSerializer.Parse<ulong>(ulong.MaxValue.ToString()));
+ Assert.Equal(ulong.MinValue, JsonSerializer.Parse<ulong?>(ulong.MinValue.ToString()));
+ Assert.Equal(ulong.MaxValue, JsonSerializer.Parse<ulong?>(ulong.MaxValue.ToString()));
+
+ Assert.Equal(decimal.MinValue, JsonSerializer.Parse<decimal>(decimal.MinValue.ToString()));
+ Assert.Equal(decimal.MaxValue, JsonSerializer.Parse<decimal>(decimal.MaxValue.ToString()));
+ Assert.Equal(decimal.MinValue, JsonSerializer.Parse<decimal?>(decimal.MinValue.ToString()));
+ Assert.Equal(decimal.MaxValue, JsonSerializer.Parse<decimal?>(decimal.MaxValue.ToString()));
+
+ // todo: these are failing; do we need round-trip format "R"?
+ //Assert.Equal(double.MinValue, JsonSerializer.Parse<double>(double.MinValue.ToString()));
+ //Assert.Equal(double.MaxValue, JsonSerializer.Parse<double>(double.MaxValue.ToString()));
+ //Assert.Equal(double.MinValue, JsonSerializer.Parse<double?>(double.MinValue.ToString()));
+ //Assert.Equal(double.MaxValue, JsonSerializer.Parse<double?>(double.MaxValue.ToString()));
+ }
+
+ [Fact]
+ public static void ValueFail()
+ {
+ string unexpectedString = @"""unexpected string""";
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<byte?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<sbyte?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<short?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ushort?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<float?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<int?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<uint?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<long?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<ulong?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<decimal?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<double?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<DateTime>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<DateTime>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<DateTime?>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<DateTime?>(unexpectedString));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<string>("1"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<string>("1"));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<char>("1"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<char>("1"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<char?>("1"));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<char?>("1"));
+
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<Enum>(unexpectedString));
+ Assert.Throws<JsonReaderException>(() => JsonSerializer.Parse<Enum>(unexpectedString));
+ }
+
+ [Fact]
+ public static void ReadObjectArray()
+ {
+ string data =
+ "[" +
+ SimpleTestClass.s_json +
+ "," +
+ SimpleTestClass.s_json +
+ "]";
+
+ SimpleTestClass[] i = JsonSerializer.Parse<SimpleTestClass[]>(Encoding.UTF8.GetBytes(data));
+
+ i[0].Verify();
+ i[1].Verify();
+ }
+
+ [Fact]
+ public static void ReadPrimitiveJaggedArray()
+ {
+ int[][] i = JsonSerializer.Parse<int[][]>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+ Assert.Equal(1, i[0][0]);
+ Assert.Equal(2, i[0][1]);
+ Assert.Equal(3, i[1][0]);
+ Assert.Equal(4, i[1][1]);
+ }
+
+ [Fact]
+ public static void ReadListOfList()
+ {
+ List<List<int>> result = JsonSerializer.Parse<List<List<int>>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ Assert.Equal(1, result[0][0]);
+ Assert.Equal(2, result[0][1]);
+ Assert.Equal(3, result[1][0]);
+ Assert.Equal(4, result[1][1]);
+ }
+
+ [Fact]
+ public static void ReadListOfArray()
+ {
+ List<int[]> result = JsonSerializer.Parse<List<int[]>>(Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ Assert.Equal(1, result[0][0]);
+ Assert.Equal(2, result[0][1]);
+ Assert.Equal(3, result[1][0]);
+ Assert.Equal(4, result[1][1]);
+ }
+
+ [Fact]
+ public static void ReadArrayOfList()
+ {
+ List<int>[] result = JsonSerializer.Parse<List<int>[]> (Encoding.UTF8.GetBytes(@"[[1,2],[3,4]]"));
+
+ Assert.Equal(1, result[0][0]);
+ Assert.Equal(2, result[0][1]);
+ Assert.Equal(3, result[1][0]);
+ Assert.Equal(4, result[1][1]);
+ }
+
+ [Fact]
+ public static void ReadPrimitiveList()
+ {
+ List<int> i = JsonSerializer.Parse<List<int>>(Encoding.UTF8.GetBytes(@"[1,2]"));
+ Assert.Equal(1, i[0]);
+ Assert.Equal(2, i[1]);
+ }
+
+ public class TestClassWithBadData
+ {
+ public TestChildClassWithBadData[] Children { get; set; }
+ }
+
+ public class TestChildClassWithBadData
+ {
+ public int MyProperty { get; set; }
+ }
+
+ [Fact]
+ public static void ReadConversionFails()
+ {
+ byte[] data = Encoding.UTF8.GetBytes(
+ @"{" +
+ @"""Children"":[" +
+ @"{""MyProperty"":""StringButShouldBeInt""}" +
+ @"]" +
+ @"}");
+
+ bool exceptionThrown = false;
+
+ try
+ {
+ JsonSerializer.Parse<TestClassWithBadData>(data);
+ }
+ catch (JsonReaderException exception)
+ {
+ exceptionThrown = true;
+
+ // Exception should contain property path.
+ Assert.True(exception.ToString().Contains("[System.Text.Json.Serialization.Tests.ValueTests+TestClassWithBadData].Children.MyProperty"));
+ }
+
+ Assert.True(exceptionThrown);
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+ public static partial class ValueTests
+ {
+ [Fact]
+ public static void WritePrimitives()
+ {
+ {
+ string json = JsonSerializer.ToString(1);
+ Assert.Equal("1", json);
+ }
+
+ {
+ Span<byte> json = JsonSerializer.ToBytes(1);
+ Assert.Equal(Encoding.UTF8.GetBytes("1"), json.ToArray());
+ }
+
+ {
+ string json = JsonSerializer.ToString(long.MaxValue);
+ Assert.Equal(long.MaxValue.ToString(), json);
+ }
+
+ {
+ Span<byte> json = JsonSerializer.ToBytes(long.MaxValue);
+ Assert.Equal(Encoding.UTF8.GetBytes(long.MaxValue.ToString()), json.ToArray());
+ }
+
+ {
+ string json = JsonSerializer.ToString("Hello");
+ Assert.Equal(@"""Hello""", json);
+ }
+
+ {
+ Span<byte> json = JsonSerializer.ToBytes("Hello");
+ Assert.Equal(Encoding.UTF8.GetBytes(@"""Hello"""), json.ToArray());
+ }
+ }
+
+ [Fact]
+ public static void WritePrimitiveArray()
+ {
+ var input = new int[] { 0, 1 };
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[0,1]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayWithEnums()
+ {
+ var input = new SampleEnum[] { SampleEnum.One, SampleEnum.Two };
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+
+ [Fact]
+ public static void WriteObjectArray()
+ {
+ string json;
+
+ {
+ SimpleTestClass[] input = new SimpleTestClass[] { new SimpleTestClass(), new SimpleTestClass() };
+ input[0].Initialize();
+ input[0].Verify();
+
+ input[1].Initialize();
+ input[1].Verify();
+
+ json = JsonSerializer.ToString(input);
+ }
+
+ {
+ SimpleTestClass[] output = JsonSerializer.Parse<SimpleTestClass[]>(json);
+ Assert.Equal(2, output.Length);
+ output[0].Verify();
+ output[1].Verify();
+ }
+ }
+
+ [Fact]
+ public static void WritePrimitiveJaggedArray()
+ {
+ var input = new int[2][];
+ input[0] = new int[] { 1, 2 };
+ input[1] = new int[] { 3, 4 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteListOfList()
+ {
+ var input = new List<List<int>>
+ {
+ new List<int>() { 1, 2 },
+ new List<int>() { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteListOfArray()
+ {
+ var input = new List<int[]>
+ {
+ new int[] { 1, 2 },
+ new int[] { 3, 4 }
+ };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WriteArrayOfList()
+ {
+ var input = new List<int>[2];
+ input[0] = new List<int>() { 1, 2 };
+ input[1] = new List<int>() { 3, 4 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[[1,2],[3,4]]", json);
+ }
+
+ [Fact]
+ public static void WritePrimitiveList()
+ {
+ var input = new List<int> { 1, 2 };
+
+ string json = JsonSerializer.ToString(input);
+ Assert.Equal("[1,2]", json);
+ }
+ }
+}
<PropertyGroup>
<ProjectGuid>{5F553243-042C-45C0-8E49-C739131E11C3}</ProjectGuid>
<Configurations>netcoreapp-Debug;netcoreapp-Release;uap-Windows_NT-Debug;uap-Windows_NT-Release;netstandard-Debug;netstandard-Release</Configurations>
+ <!-- For the inbox library (that is shipping with the product), this should always be true. -->
+ <!-- BUILDING_INBOX_LIBRARY is only false when building for netstandard to validate that the sources are netstandard compatible. -->
+ <!-- This is meant to help with producing a source package and not to ship a netstandard compatible binary. -->
+ <DefineConstants Condition="'$(TargetsNETStandard)' != 'true'">$(DefineConstants);BUILDING_INBOX_LIBRARY</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\IO\WrappedMemoryStream.cs">
<Compile Include="JsonTestHelper.cs" />
<Compile Include="JsonWriterStateTests.cs" />
<Compile Include="ResizableArray.cs" />
+ <Compile Include="Serialization\Array.ReadTests.cs" />
+ <Compile Include="Serialization\Array.WriteTests.cs" />
+ <Compile Include="Serialization\CyclicTests.cs" />
+ <Compile Include="Serialization\EnumTests.cs" />
+ <Compile Include="Serialization\Null.ReadTests.cs" />
+ <Compile Include="Serialization\Null.WriteTests.cs" />
+ <Compile Include="Serialization\PolymorphicTests.cs" />
+ <Compile Include="Serialization\PropertyVisibilityTests.cs" />
+ <Compile Include="Serialization\SpanTests.cs" />
+ <Compile Include="Serialization\Stream.ReadTests.cs" />
+ <Compile Include="Serialization\Stream.WriteTests.cs" />
+ <Compile Include="Serialization\String.ReadTests.cs" />
+ <Compile Include="Serialization\String.WriteTests.cs" />
+ <Compile Include="Serialization\TestClasses.cs" />
+ <Compile Include="Serialization\TestClasses.Polymorphic.cs" />
+ <Compile Include="Serialization\TestData.cs" />
+ <Compile Include="Serialization\Value.ReadTests.cs" />
+ <Compile Include="Serialization\Value.WriteTests.cs" />
<Compile Include="TestCaseType.cs" />
<Compile Include="Utf8JsonReaderTests.cs" />
<Compile Include="Utf8JsonReaderTests.MultiSegment.cs" />