Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Initial Json serialization functionality #35609

Merged
merged 11 commits into from
Mar 2, 2019
37 changes: 35 additions & 2 deletions src/System.Text.Json/ref/System.Text.Json.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public void Dispose() { }
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; }
Expand Down Expand Up @@ -64,6 +65,7 @@ public readonly partial struct JsonElement
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() { }
Expand All @@ -76,6 +78,7 @@ public void Reset() { }
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() { }
Expand All @@ -102,7 +105,7 @@ public override void GetObjectData(System.Runtime.Serialization.SerializationInf
}
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 { } }
}
Expand Down Expand Up @@ -142,13 +145,14 @@ public enum JsonValueType : byte
}
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; } }
Expand All @@ -157,6 +161,7 @@ public partial struct JsonWriterState
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; } }
Expand Down Expand Up @@ -192,6 +197,7 @@ public ref partial struct Utf8JsonReader
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; } }
Expand Down Expand Up @@ -281,3 +287,30 @@ public void WriteStringValue(System.ReadOnlySpan<char> value, bool escape = true
public void WriteStringValue(string value, bool escape = true) { }
}
}
namespace System.Text.Json.Serialization
{
public static partial class JsonSerializer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How's the code coverage for these new APIs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch coverage is 80%-100% depending on file, but somewhat hampered because some tests are disabled because some of the APIs are not yet public (they were made internal). I do need more negative tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait this PR removed the PipeReader and PipeWriter overloads 😢

{
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; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we decide against keeping the encoding in the name? ToUtf8Bytes?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively, accept a destination span or would that be an overload if we decide to add it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was unresolved during the review. I have a "todo" in the declaration. I can't remove it because we need some easy way to get the raw bytes out for practicality.

There was also a concern about the naming - we didn't want to "use up" a "ToJson" or similar if we add a utf8Char type. I think "ToUtf8Bytes" would work however although it seems somewhat unnecessary to me.

And yes we will probably want an overload to copy into a user-specified buffer\memory - I'll bring this up in the next review.

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 { } }
}
}
1 change: 1 addition & 0 deletions src/System.Text.Json/ref/System.Text.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetGroup)' == 'netstandard'">
<Reference Include="System.Memory" />
<Reference Include="System.Threading.Tasks.Extensions" />
</ItemGroup>
</Project>
104 changes: 104 additions & 0 deletions src/System.Text.Json/src/JsonReflectionEmitMaterializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// 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}"));
}
}
}
23 changes: 22 additions & 1 deletion src/System.Text.Json/src/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -282,4 +282,25 @@
<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>
Loading