-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Initial Json serialization functionality #35609
Changes from all commits
ebfb03c
b363286
fc8b356
ffa7e2b
b66f0c3
3f9990d
9babcb6
eaecf6b
40fac68
712c2f6
796dafc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; } | ||
|
@@ -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() { } | ||
|
@@ -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() { } | ||
|
@@ -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 { } } | ||
} | ||
|
@@ -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; } } | ||
|
@@ -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; } } | ||
|
@@ -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; } } | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; } | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we decide against keeping the encoding in the name? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { } } | ||
} | ||
} |
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 | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
public override JsonClassInfo.ConstructorDelegate CreateConstructor(Type type) | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
ConstructorInfo realMethod = type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic| BindingFlags.Instance, binder: null, Type.EmptyTypes, modifiers: null); | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (realMethod == null) | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return null; | ||
|
||
var dynamicMethod = new DynamicMethod( | ||
realMethod.Name, | ||
type, | ||
Type.EmptyTypes, | ||
typeof(JsonReflectionEmitMaterializer).Module, | ||
skipVisibility: true); | ||
|
||
if (dynamicMethod != null) | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
ILGenerator generator = dynamicMethod?.GetILGenerator(); | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (generator != null) | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
|
||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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}")); | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
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(); | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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; | ||
} | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
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; | ||
steveharter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
throw new InvalidOperationException(SR.Format(SR.SerializationUnableToCreateDynamicMethod, $"{propertyInfo.Name}.{realMethod.Name}")); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.