1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
- #if ! NETCOREAPP
5
4
using System . Diagnostics ;
6
- #endif
7
5
using System . Diagnostics . CodeAnalysis ;
8
6
using System . IO ;
9
7
using System . Net . Http . Headers ;
10
8
using System . Text ;
11
9
using System . Text . Json ;
10
+ using System . Text . Json . Serialization . Metadata ;
12
11
using System . Threading ;
13
12
using System . Threading . Tasks ;
14
13
15
14
namespace System . Net . Http . Json
16
15
{
17
16
public sealed partial class JsonContent : HttpContent
18
17
{
19
- private readonly JsonSerializerOptions ? _jsonSerializerOptions ;
20
- public Type ObjectType { get ; }
18
+ private readonly JsonTypeInfo _typeInfo ;
19
+ public Type ObjectType => _typeInfo . Type ;
21
20
public object ? Value { get ; }
22
21
23
- [ RequiresUnreferencedCode ( HttpContentJsonExtensions . SerializationUnreferencedCodeMessage ) ]
24
- [ RequiresDynamicCode ( HttpContentJsonExtensions . SerializationDynamicCodeMessage ) ]
25
22
private JsonContent (
26
23
object ? inputValue ,
27
- Type inputType ,
28
- MediaTypeHeaderValue ? mediaType ,
29
- JsonSerializerOptions ? options )
24
+ JsonTypeInfo jsonTypeInfo ,
25
+ MediaTypeHeaderValue ? mediaType )
30
26
{
31
- if ( inputType is null )
32
- {
33
- throw new ArgumentNullException ( nameof ( inputType ) ) ;
34
- }
35
-
36
- if ( inputValue != null && ! inputType . IsAssignableFrom ( inputValue . GetType ( ) ) )
37
- {
38
- throw new ArgumentException ( SR . Format ( SR . SerializeWrongType , inputType , inputValue . GetType ( ) ) ) ;
39
- }
27
+ Debug . Assert ( jsonTypeInfo is not null ) ;
28
+ Debug . Assert ( inputValue is null || jsonTypeInfo . Type . IsAssignableFrom ( inputValue . GetType ( ) ) ) ;
40
29
41
30
Value = inputValue ;
42
- ObjectType = inputType ;
31
+ _typeInfo = jsonTypeInfo ;
43
32
Headers . ContentType = mediaType ?? JsonHelpers . GetDefaultMediaType ( ) ;
44
- _jsonSerializerOptions = options ?? JsonHelpers . s_defaultSerializerOptions ;
45
33
}
46
34
47
35
[ RequiresUnreferencedCode ( HttpContentJsonExtensions . SerializationUnreferencedCodeMessage ) ]
48
36
[ RequiresDynamicCode ( HttpContentJsonExtensions . SerializationDynamicCodeMessage ) ]
49
37
public static JsonContent Create < T > ( T inputValue , MediaTypeHeaderValue ? mediaType = null , JsonSerializerOptions ? options = null )
50
- => Create ( inputValue , typeof ( T ) , mediaType , options ) ;
38
+ => Create ( inputValue , GetJsonTypeInfo ( typeof ( T ) , options ) , mediaType ) ;
51
39
52
40
[ RequiresUnreferencedCode ( HttpContentJsonExtensions . SerializationUnreferencedCodeMessage ) ]
53
41
[ RequiresDynamicCode ( HttpContentJsonExtensions . SerializationDynamicCodeMessage ) ]
54
42
public static JsonContent Create ( object ? inputValue , Type inputType , MediaTypeHeaderValue ? mediaType = null , JsonSerializerOptions ? options = null )
55
- => new JsonContent ( inputValue , inputType , mediaType , options ) ;
43
+ {
44
+ ThrowHelper . ThrowIfNull ( inputType ) ;
45
+ EnsureTypeCompatibility ( inputValue , inputType ) ;
46
+
47
+ return new JsonContent ( inputValue , GetJsonTypeInfo ( inputType , options ) , mediaType ) ;
48
+ }
49
+
50
+ public static JsonContent Create < T > ( T ? inputValue , JsonTypeInfo < T > jsonTypeInfo ,
51
+ MediaTypeHeaderValue ? mediaType = null )
52
+ {
53
+ ThrowHelper . ThrowIfNull ( jsonTypeInfo ) ;
54
+
55
+ return new JsonContent ( inputValue , jsonTypeInfo , mediaType ) ;
56
+ }
57
+
58
+ public static JsonContent Create ( object ? inputValue , JsonTypeInfo jsonTypeInfo ,
59
+ MediaTypeHeaderValue ? mediaType = null )
60
+ {
61
+ ThrowHelper . ThrowIfNull ( jsonTypeInfo ) ;
62
+ EnsureTypeCompatibility ( inputValue , jsonTypeInfo . Type ) ;
63
+
64
+ return new JsonContent ( inputValue , jsonTypeInfo , mediaType ) ;
65
+ }
56
66
57
67
protected override Task SerializeToStreamAsync ( Stream stream , TransportContext ? context )
58
68
=> SerializeToStreamAsyncCore ( stream , async: true , CancellationToken . None ) ;
@@ -63,10 +73,6 @@ protected override bool TryComputeLength(out long length)
63
73
return false ;
64
74
}
65
75
66
- [ UnconditionalSuppressMessage ( "ReflectionAnalysis" , "IL2026:RequiresUnreferencedCode" ,
67
- Justification = "The ctor is annotated with RequiresUnreferencedCode." ) ]
68
- [ UnconditionalSuppressMessage ( "AotAnalysis" , "IL3050:RequiresDynamicCode" ,
69
- Justification = "The ctor is annotated with RequiresDynamicCode." ) ]
70
76
private async Task SerializeToStreamAsyncCore ( Stream targetStream , bool async , CancellationToken cancellationToken )
71
77
{
72
78
Encoding ? targetEncoding = JsonHelpers . GetEncoding ( this ) ;
@@ -80,11 +86,11 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
80
86
{
81
87
if ( async)
82
88
{
83
- await JsonSerializer . SerializeAsync ( transcodingStream , Value , ObjectType , _jsonSerializerOptions , cancellationToken ) . ConfigureAwait ( false ) ;
89
+ await JsonSerializer . SerializeAsync ( transcodingStream , Value , _typeInfo , cancellationToken ) . ConfigureAwait ( false ) ;
84
90
}
85
91
else
86
92
{
87
- JsonSerializer . Serialize ( transcodingStream , Value , ObjectType , _jsonSerializerOptions ) ;
93
+ JsonSerializer . Serialize ( transcodingStream , Value , _typeInfo ) ;
88
94
}
89
95
}
90
96
finally
@@ -101,11 +107,11 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
101
107
}
102
108
}
103
109
#else
104
- Debug. Assert ( async ) ;
110
+ Debug. Assert ( async, "HttpContent synchronous serialization is only supported since .NET 5.0" ) ;
105
111
106
112
using ( TranscodingWriteStream transcodingStream = new TranscodingWriteStream ( targetStream , targetEncoding ) )
107
113
{
108
- await JsonSerializer . SerializeAsync ( transcodingStream , Value , ObjectType , _jsonSerializerOptions , cancellationToken ) . ConfigureAwait( false) ;
114
+ await JsonSerializer. SerializeAsync ( transcodingStream , Value , _typeInfo , cancellationToken ) . ConfigureAwait ( false ) ;
109
115
// The transcoding streams use Encoders and Decoders that have internal buffers. We need to flush these
110
116
// when there is no more data to be written. Stream.FlushAsync isn't suitable since it's
111
117
// acceptable to Flush a Stream (multiple times) prior to completion.
@@ -117,17 +123,39 @@ private async Task SerializeToStreamAsyncCore(Stream targetStream, bool async, C
117
123
{
118
124
if ( async )
119
125
{
120
- await JsonSerializer . SerializeAsync ( targetStream , Value , ObjectType , _jsonSerializerOptions , cancellationToken ) . ConfigureAwait ( false ) ;
126
+ await JsonSerializer . SerializeAsync ( targetStream , Value , _typeInfo , cancellationToken ) . ConfigureAwait ( false ) ;
121
127
}
122
128
else
123
129
{
124
130
#if NETCOREAPP
125
- JsonSerializer . Serialize ( targetStream , Value , ObjectType , _jsonSerializerOptions ) ;
131
+ JsonSerializer . Serialize ( targetStream , Value , _typeInfo ) ;
126
132
#else
127
- Debug . Fail ( "Synchronous serialization is only supported since .NET 5.0" ) ;
133
+ Debug . Fail ( "HttpContent synchronous serialization is only supported since .NET 5.0" ) ;
128
134
#endif
129
135
}
130
136
}
131
137
}
138
+
139
+ [ RequiresUnreferencedCode ( HttpContentJsonExtensions . SerializationUnreferencedCodeMessage ) ]
140
+ [ RequiresDynamicCode ( HttpContentJsonExtensions . SerializationDynamicCodeMessage ) ]
141
+ private static JsonTypeInfo GetJsonTypeInfo( Type inputType , JsonSerializerOptions ? options )
142
+ {
143
+ Debug. Assert ( inputType is not null ) ;
144
+
145
+ // Ensure the options supports the call to GetTypeInfo
146
+ options ??= JsonHelpers. s_defaultSerializerOptions ;
147
+ options. TypeInfoResolver ??= JsonSerializerOptions . Default . TypeInfoResolver ;
148
+ options. MakeReadOnly ( ) ;
149
+
150
+ return options. GetTypeInfo ( inputType ) ;
151
+ }
152
+
153
+ private static void EnsureTypeCompatibility( object ? inputValue , Type inputType )
154
+ {
155
+ if ( inputValue is not null && ! inputType . IsAssignableFrom ( inputValue . GetType ( ) ) )
156
+ {
157
+ throw new ArgumentException( SR . Format ( SR . SerializeWrongType , inputType , inputValue . GetType ( ) ) ) ;
158
+ }
159
+ }
132
160
}
133
161
}
0 commit comments