4
4
using System . Text . Encodings . Web ;
5
5
using System . Text . Json ;
6
6
using System . Text . Json . Serialization ;
7
+ using JsonApiDotNetCore . Configuration ;
8
+ using JsonApiDotNetCore . Resources ;
9
+ using JsonApiDotNetCore . Resources . Annotations ;
7
10
using Microsoft . Extensions . Logging ;
8
11
9
12
namespace JsonApiDotNetCore . Middleware ;
@@ -14,8 +17,105 @@ internal abstract class TraceLogWriter
14
17
{
15
18
WriteIndented = true ,
16
19
Encoder = JavaScriptEncoder . UnsafeRelaxedJsonEscaping ,
17
- ReferenceHandler = ReferenceHandler . Preserve
20
+ ReferenceHandler = ReferenceHandler . IgnoreCycles ,
21
+ DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull ,
22
+ Converters =
23
+ {
24
+ new JsonStringEnumConverter ( ) ,
25
+ new ResourceTypeInTraceJsonConverter ( ) ,
26
+ new ResourceFieldInTraceJsonConverterFactory ( ) ,
27
+ new AbstractResourceWrapperInTraceJsonConverterFactory ( ) ,
28
+ new IdentifiableInTraceJsonConverter ( )
29
+ }
18
30
} ;
31
+
32
+ private sealed class ResourceTypeInTraceJsonConverter : JsonConverter < ResourceType >
33
+ {
34
+ public override ResourceType Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
35
+ {
36
+ throw new NotSupportedException ( ) ;
37
+ }
38
+
39
+ public override void Write ( Utf8JsonWriter writer , ResourceType value , JsonSerializerOptions options )
40
+ {
41
+ writer . WriteStringValue ( value . PublicName ) ;
42
+ }
43
+ }
44
+
45
+ private sealed class ResourceFieldInTraceJsonConverterFactory : JsonConverterFactory
46
+ {
47
+ public override bool CanConvert ( Type typeToConvert )
48
+ {
49
+ return typeToConvert . IsAssignableTo ( typeof ( ResourceFieldAttribute ) ) ;
50
+ }
51
+
52
+ public override JsonConverter CreateConverter ( Type typeToConvert , JsonSerializerOptions options )
53
+ {
54
+ Type converterType = typeof ( ResourceFieldInTraceJsonConverter < > ) . MakeGenericType ( typeToConvert ) ;
55
+ return ( JsonConverter ) Activator . CreateInstance ( converterType ) ! ;
56
+ }
57
+
58
+ private sealed class ResourceFieldInTraceJsonConverter < TField > : JsonConverter < TField >
59
+ where TField : ResourceFieldAttribute
60
+ {
61
+ public override TField Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
62
+ {
63
+ throw new NotSupportedException ( ) ;
64
+ }
65
+
66
+ public override void Write ( Utf8JsonWriter writer , TField value , JsonSerializerOptions options )
67
+ {
68
+ writer . WriteStringValue ( value . PublicName ) ;
69
+ }
70
+ }
71
+ }
72
+
73
+ private sealed class IdentifiableInTraceJsonConverter : JsonConverter < IIdentifiable >
74
+ {
75
+ public override IIdentifiable Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
76
+ {
77
+ throw new NotSupportedException ( ) ;
78
+ }
79
+
80
+ public override void Write ( Utf8JsonWriter writer , IIdentifiable value , JsonSerializerOptions options )
81
+ {
82
+ // Intentionally *not* calling GetClrType() because we need delegation to the wrapper converter.
83
+ Type runtimeType = value . GetType ( ) ;
84
+
85
+ JsonSerializer . Serialize ( writer , value , runtimeType , options ) ;
86
+ }
87
+ }
88
+
89
+ private sealed class AbstractResourceWrapperInTraceJsonConverterFactory : JsonConverterFactory
90
+ {
91
+ public override bool CanConvert ( Type typeToConvert )
92
+ {
93
+ return typeToConvert . IsAssignableTo ( typeof ( IAbstractResourceWrapper ) ) ;
94
+ }
95
+
96
+ public override JsonConverter CreateConverter ( Type typeToConvert , JsonSerializerOptions options )
97
+ {
98
+ Type converterType = typeof ( AbstractResourceWrapperInTraceJsonConverter < > ) . MakeGenericType ( typeToConvert ) ;
99
+ return ( JsonConverter ) Activator . CreateInstance ( converterType ) ! ;
100
+ }
101
+
102
+ private sealed class AbstractResourceWrapperInTraceJsonConverter < TWrapper > : JsonConverter < TWrapper >
103
+ where TWrapper : IAbstractResourceWrapper
104
+ {
105
+ public override TWrapper Read ( ref Utf8JsonReader reader , Type typeToConvert , JsonSerializerOptions options )
106
+ {
107
+ throw new NotSupportedException ( ) ;
108
+ }
109
+
110
+ public override void Write ( Utf8JsonWriter writer , TWrapper value , JsonSerializerOptions options )
111
+ {
112
+ writer . WriteStartObject ( ) ;
113
+ writer . WriteString ( "ClrType" , value . AbstractType . FullName ) ;
114
+ writer . WriteString ( "StringId" , value . StringId ) ;
115
+ writer . WriteEndObject ( ) ;
116
+ }
117
+ }
118
+ }
19
119
}
20
120
21
121
internal sealed class TraceLogWriter < T > : TraceLogWriter
@@ -88,26 +188,12 @@ private static void WriteProperty(StringBuilder builder, PropertyInfo property,
88
188
builder . Append ( ": " ) ;
89
189
90
190
object ? value = property . GetValue ( instance ) ;
91
-
92
- if ( value == null )
93
- {
94
- builder . Append ( "null" ) ;
95
- }
96
- else if ( value is string stringValue )
97
- {
98
- builder . Append ( '"' ) ;
99
- builder . Append ( stringValue ) ;
100
- builder . Append ( '"' ) ;
101
- }
102
- else
103
- {
104
- WriteObject ( builder , value ) ;
105
- }
191
+ WriteObject ( builder , value ) ;
106
192
}
107
193
108
- private static void WriteObject ( StringBuilder builder , object value )
194
+ private static void WriteObject ( StringBuilder builder , object ? value )
109
195
{
110
- if ( HasToStringOverload ( value . GetType ( ) ) )
196
+ if ( value != null && value is not string && HasToStringOverload ( value . GetType ( ) ) )
111
197
{
112
198
builder . Append ( value ) ;
113
199
}
@@ -118,28 +204,19 @@ private static void WriteObject(StringBuilder builder, object value)
118
204
}
119
205
}
120
206
121
- private static bool HasToStringOverload ( Type ? type )
207
+ private static bool HasToStringOverload ( Type type )
122
208
{
123
- if ( type != null )
124
- {
125
- MethodInfo ? toStringMethod = type . GetMethod ( "ToString" , Array . Empty < Type > ( ) ) ;
126
-
127
- if ( toStringMethod != null && toStringMethod . DeclaringType != typeof ( object ) )
128
- {
129
- return true ;
130
- }
131
- }
132
-
133
- return false ;
209
+ MethodInfo ? toStringMethod = type . GetMethod ( "ToString" , Array . Empty < Type > ( ) ) ;
210
+ return toStringMethod != null && toStringMethod . DeclaringType != typeof ( object ) ;
134
211
}
135
212
136
- private static string SerializeObject ( object value )
213
+ private static string SerializeObject ( object ? value )
137
214
{
138
215
try
139
216
{
140
217
return JsonSerializer . Serialize ( value , SerializerOptions ) ;
141
218
}
142
- catch ( JsonException )
219
+ catch ( Exception exception ) when ( exception is JsonException or NotSupportedException )
143
220
{
144
221
// Never crash as a result of logging, this is best-effort only.
145
222
return "object" ;
0 commit comments