1
- // COPYRIGHT 2018 by the Open Rails project.
1
+ // COPYRIGHT 2018 by the Open Rails project.
2
2
//
3
3
// This file is part of Open Rails.
4
4
//
18
18
// Use this define to diagnose issues in the JSON reader below.
19
19
//#define DEBUG_JSON_READER
20
20
21
+ using Microsoft . Xna . Framework ;
22
+ using Newtonsoft . Json ;
21
23
using System ;
22
24
using System . Collections . Generic ;
23
25
using System . Diagnostics ;
24
26
using System . IO ;
25
27
using System . Linq ;
26
28
using System . Text ;
27
- using Newtonsoft . Json ;
28
29
29
30
namespace Orts . Parsers . OR
30
31
{
@@ -37,26 +38,44 @@ public class JsonReader
37
38
/// <param name="tryParse"></param>
38
39
public static void ReadFile ( string fileName , Func < JsonReader , bool > tryParse )
39
40
{
40
- using ( var reader = new JsonTextReader ( File . OpenText ( fileName ) )
41
+ using ( var reader = new JsonTextReader ( File . OpenText ( fileName ) ) )
41
42
{
42
- CloseInput = true ,
43
- } )
43
+ new JsonReader ( fileName , reader ) . ReadFile ( tryParse ) ;
44
+ }
45
+ }
46
+
47
+ /// <summary>
48
+ /// Read the JSON from a string using a method TryParse() which is specific for the expected objects.
49
+ /// </summary>
50
+ /// <param name="content"></param>
51
+ /// <param name="fileName"></param>
52
+ /// <param name="tryParse"></param>
53
+ public static ( int Warning , int Information ) ReadTest ( string content , string fileName , Func < JsonReader , bool > tryParse )
54
+ {
55
+ using ( var reader = new JsonTextReader ( new StringReader ( content ) ) )
44
56
{
45
- new JsonReader ( fileName , reader ) . ReadBlock ( tryParse ) ;
57
+ var json = new JsonReader ( fileName , reader ) ;
58
+ json . ReadFile ( tryParse ) ;
59
+ return ( json . _countWarnings , json . _countInformations ) ;
46
60
}
47
61
}
48
62
49
63
string _fileName ;
50
64
JsonTextReader _reader ;
51
65
StringBuilder _path ;
52
66
Stack < int > _pathPositions ;
67
+ Stack < string > _paths ;
68
+ int _countWarnings ;
69
+ int _countInformations ;
70
+
71
+ string FullPath { get => _path . Length > 0 ? _path . ToString ( ) : "(root)" ; }
53
72
54
73
/// <summary>
55
- /// Contains a condensed account of the position of the current item in the JSO , such as when parsing "Clear" from a WeatherFile:
74
+ /// Contains a condensed account of the position of the current item in the JSON , such as when parsing "Clear" from a WeatherFile:
56
75
/// JsonReader item;
57
76
/// item.Path = "Changes[].Type"
58
77
/// </summary>
59
- public string Path { get ; private set ; }
78
+ public string Path { get => _paths . Peek ( ) ; }
60
79
61
80
/// <summary>
62
81
/// Note the values needed for parsing and helpful error messages
@@ -69,21 +88,43 @@ public static void ReadFile(string fileName, Func<JsonReader, bool> tryParse)
69
88
_reader = reader ;
70
89
_path = new StringBuilder ( ) ;
71
90
_pathPositions = new Stack < int > ( ) ;
91
+ _paths = new Stack < string > ( ) ;
72
92
}
73
93
94
+ void ReadFile ( Func < JsonReader , bool > tryParse )
95
+ {
96
+ try
97
+ {
98
+ ReadBlock ( tryParse ) ;
99
+ // Read the rest of the file so that we catch any extra data, which might be in error
100
+ while ( _reader . Read ( ) ) ;
101
+ }
102
+ catch ( JsonReaderException error )
103
+ {
104
+ // Newtonsoft.Json unfortunately includes extra information in the message we already provide
105
+ var jsonMessage = error . Message . Split ( new [ ] { ". Path '" } , StringSplitOptions . None ) ;
106
+ TraceWarning ( $ "{ jsonMessage [ 0 ] } in { FullPath } ") ;
107
+ }
108
+ }
109
+
110
+ /// <summary>
111
+ /// Reads next token and stores in _reader.TokenType, _reader.ValueType, _reader.Value
112
+ /// Throws exception if value not as expected.
113
+ /// PropertyNames are case-sensitive.
114
+ /// </summary>
115
+ /// <param name="tryParse"></param>
74
116
public void ReadBlock ( Func < JsonReader , bool > tryParse )
75
117
{
76
118
var basePosition = _pathPositions . Count > 0 ? _pathPositions . Peek ( ) : 0 ;
77
119
78
120
#if DEBUG_JSON_READER
79
- Console . WriteLine ( ) ;
80
- Console . WriteLine ( $ "JsonReader({ _path . ToString ( ) } ({ string . Join ( "," , _pathPositions . Select ( p => p . ToString ( ) ) . ToArray ( ) ) } )).ReadBlock(): base={ basePosition } ") ;
121
+ Console . WriteLine ( $ "JsonReader({ basePosition } / { _path } / { String . Join ( " " , _pathPositions ) } ).ReadBlock()") ;
81
122
#endif
82
123
83
124
while ( _reader . Read ( ) ) // Reads the next JSON token. Returns false if at end
84
125
{
85
126
#if DEBUG_JSON_READER
86
- Console . WriteLine ( $ "JsonReader.ReadBlock( { _path . ToString ( ) } ( { string . Join ( ", " , _pathPositions . Select ( p => p . ToString ( ) ) . ToArray ( ) ) } )): token= { _reader . TokenType } value= { _reader . Value } type= { _reader . ValueType } ") ;
127
+ Console . Write ( $ "JsonReader( { basePosition } / { _path } / { String . Join ( " " , _pathPositions ) } ) --> ") ;
87
128
#endif
88
129
switch ( _reader . TokenType )
89
130
{
@@ -105,27 +146,41 @@ public void ReadBlock(Func<JsonReader, bool> tryParse)
105
146
_path . Append ( ( string ) _reader . Value ) ;
106
147
break ;
107
148
case JsonToken . EndObject :
108
- var end = _pathPositions . Pop ( ) ;
149
+ _pathPositions . Pop ( ) ;
109
150
_path . Length = _pathPositions . Pop ( ) ;
110
- if ( end == basePosition ) return ;
111
151
break ;
112
152
}
153
+ #if DEBUG_JSON_READER
154
+ Console . WriteLine ( $ "({ basePosition } / { _path } / { string . Join ( " " , _pathPositions ) } ) token={ _reader . TokenType } value={ _reader . Value } type={ _reader . ValueType } ") ;
155
+ #endif
156
+ if ( _path . Length <= basePosition && ( _reader . TokenType == JsonToken . EndArray || _reader . TokenType == JsonToken . EndObject ) ) return ;
113
157
114
158
switch ( _reader . TokenType )
115
159
{
116
160
case JsonToken . StartObject :
161
+ case JsonToken . StartArray :
117
162
case JsonToken . Boolean :
118
163
case JsonToken . Bytes :
119
164
case JsonToken . Date :
120
165
case JsonToken . Float :
121
166
case JsonToken . Integer :
122
167
case JsonToken . Null :
123
168
case JsonToken . String :
124
- Path = _path . ToString ( ) . Substring ( basePosition ) ;
125
- if ( ! tryParse ( this ) ) TraceInformation ( $ "Skipped unknown { _reader . TokenType } \" { _reader . Value } \" in { Path } ") ;
169
+ _paths . Push ( _path . ToString ( ) . Substring ( basePosition ) ) ;
170
+ if ( ! tryParse ( this ) ) TraceInformation ( $ "Skipped unknown { _reader . TokenType } \" { _reader . Value } \" in { FullPath } ") ;
171
+ _paths . Pop ( ) ;
126
172
break ;
127
173
}
128
174
}
175
+
176
+ TraceWarning ( $ "Unexpected end of file in { FullPath } ") ;
177
+ }
178
+
179
+ public bool TryRead < T > ( Func < JsonReader , T > read , out T output )
180
+ {
181
+ var warnings = _countWarnings ;
182
+ output = read ( this ) ;
183
+ return warnings == _countWarnings ;
129
184
}
130
185
131
186
public T AsEnum < T > ( T defaultValue )
@@ -137,7 +192,7 @@ public T AsEnum<T>(T defaultValue)
137
192
var value = ( string ) _reader . Value ;
138
193
return ( T ) Enum . Parse ( typeof ( T ) , value , true ) ;
139
194
default :
140
- TraceWarning ( $ "Expected string (enum) value in { Path } ; got { _reader . TokenType } ") ;
195
+ TraceWarning ( $ "Expected string (enum) value in { FullPath } ; got { _reader . TokenType } ") ;
141
196
return defaultValue ;
142
197
}
143
198
}
@@ -151,7 +206,7 @@ public float AsFloat(float defaultValue)
151
206
case JsonToken . Integer :
152
207
return ( long ) _reader . Value ;
153
208
default :
154
- TraceWarning ( $ "Expected floating point value in { Path } ; got { _reader . TokenType } ") ;
209
+ TraceWarning ( $ "Expected floating point value in { FullPath } ; got { _reader . TokenType } ") ;
155
210
return defaultValue ;
156
211
}
157
212
}
@@ -163,7 +218,19 @@ public int AsInteger(int defaultValue)
163
218
case JsonToken . Integer :
164
219
return ( int ) ( long ) _reader . Value ;
165
220
default :
166
- TraceWarning ( $ "Expected integer value in { Path } ; got { _reader . TokenType } ") ;
221
+ TraceWarning ( $ "Expected integer value in { FullPath } ; got { _reader . TokenType } ") ;
222
+ return defaultValue ;
223
+ }
224
+ }
225
+
226
+ public bool AsBoolean ( bool defaultValue )
227
+ {
228
+ switch ( _reader . TokenType )
229
+ {
230
+ case JsonToken . Boolean :
231
+ return ( bool ) _reader . Value ;
232
+ default :
233
+ TraceWarning ( $ "Expected Boolean value in { FullPath } ; got { _reader . TokenType } ") ;
167
234
return defaultValue ;
168
235
}
169
236
}
@@ -175,7 +242,7 @@ public string AsString(string defaultValue)
175
242
case JsonToken . String :
176
243
return ( string ) _reader . Value ;
177
244
default :
178
- TraceWarning ( $ "Expected string value in { Path } ; got { _reader . TokenType } ") ;
245
+ TraceWarning ( $ "Expected string value in { FullPath } ; got { _reader . TokenType } ") ;
179
246
return defaultValue ;
180
247
}
181
248
}
@@ -189,19 +256,47 @@ public float AsTime(float defaultValue)
189
256
var StartTime = new TimeSpan ( int . Parse ( time [ 0 ] ) , time . Length > 1 ? int . Parse ( time [ 1 ] ) : 0 , time . Length > 2 ? int . Parse ( time [ 2 ] ) : 0 ) ;
190
257
return ( float ) StartTime . TotalSeconds ;
191
258
default :
192
- TraceWarning ( $ "Expected string (time) value in { Path } ; got { _reader . TokenType } ") ;
259
+ TraceWarning ( $ "Expected string (time) value in { FullPath } ; got { _reader . TokenType } ") ;
260
+ return defaultValue ;
261
+ }
262
+ }
263
+
264
+ public Vector3 AsVector3 ( Vector3 defaultValue )
265
+ {
266
+ switch ( _reader . TokenType )
267
+ {
268
+ case JsonToken . StartArray :
269
+ if ( TryRead ( json =>
270
+ {
271
+ var floats = new List < float > ( 3 ) ;
272
+ ReadBlock ( item =>
273
+ {
274
+ floats . Add ( item . AsFloat ( 0 ) ) ;
275
+ return true ;
276
+ } ) ;
277
+ return floats ;
278
+ } , out var vector ) )
279
+ {
280
+ if ( vector . Count == 3 ) return new Vector3 ( vector [ 0 ] , vector [ 1 ] , vector [ 2 ] ) ;
281
+ TraceWarning ( $ "Expected 3 float array (Vector3) value in { FullPath } ; got { vector . Count } float array") ;
282
+ }
283
+ return defaultValue ;
284
+ default :
285
+ TraceWarning ( $ "Expected array (Vector3) value in { FullPath } ; got { _reader . TokenType } ") ;
193
286
return defaultValue ;
194
287
}
195
288
}
196
289
197
290
public void TraceWarning ( string message )
198
291
{
199
292
Trace . TraceWarning ( "{2} in {0}:line {1}" , _fileName , _reader . LineNumber , message ) ;
293
+ _countWarnings ++ ;
200
294
}
201
295
202
296
public void TraceInformation ( string message )
203
297
{
204
298
Trace . TraceInformation ( "{2} in {0}:line {1}" , _fileName , _reader . LineNumber , message ) ;
299
+ _countInformations ++ ;
205
300
}
206
301
}
207
302
}
0 commit comments