1- // COPYRIGHT 2018 by the Open Rails project.
1+ // COPYRIGHT 2018 by the Open Rails project.
22//
33// This file is part of Open Rails.
44//
@@ -38,27 +38,44 @@ public class JsonReader
3838 /// <param name="tryParse"></param>
3939 public static void ReadFile ( string fileName , Func < JsonReader , bool > tryParse )
4040 {
41- using ( var reader = new JsonTextReader ( File . OpenText ( fileName ) )
41+ using ( var reader = new JsonTextReader ( File . OpenText ( fileName ) ) )
4242 {
43- CloseInput = true ,
44- } )
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 ) ) )
4556 {
46- new JsonReader ( fileName , reader ) . ReadBlock ( tryParse ) ;
57+ var json = new JsonReader ( fileName , reader ) ;
58+ json . ReadFile ( tryParse ) ;
59+ return ( json . _countWarnings , json . _countInformations ) ;
4760 }
4861 }
4962
5063 string _fileName ;
5164 JsonTextReader _reader ;
5265 StringBuilder _path ;
5366 Stack < int > _pathPositions ;
67+ Stack < string > _paths ;
5468 int _countWarnings ;
69+ int _countInformations ;
70+
71+ string FullPath { get => _path . Length > 0 ? _path . ToString ( ) : "(root)" ; }
5572
5673 /// <summary>
57- /// 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:
5875 /// JsonReader item;
5976 /// item.Path = "Changes[].Type"
6077 /// </summary>
61- public string Path { get ; private set ; }
78+ public string Path { get => _paths . Peek ( ) ; }
6279
6380 /// <summary>
6481 /// Note the values needed for parsing and helpful error messages
@@ -71,6 +88,23 @@ public static void ReadFile(string fileName, Func<JsonReader, bool> tryParse)
7188 _reader = reader ;
7289 _path = new StringBuilder ( ) ;
7390 _pathPositions = new Stack < int > ( ) ;
91+ _paths = new Stack < string > ( ) ;
92+ }
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+ }
74108 }
75109
76110 /// <summary>
@@ -84,14 +118,13 @@ public void ReadBlock(Func<JsonReader, bool> tryParse)
84118 var basePosition = _pathPositions . Count > 0 ? _pathPositions . Peek ( ) : 0 ;
85119
86120#if DEBUG_JSON_READER
87- Console . WriteLine ( ) ;
88- 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()") ;
89122#endif
90123
91124 while ( _reader . Read ( ) ) // Reads the next JSON token. Returns false if at end
92125 {
93126#if DEBUG_JSON_READER
94- 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 ) } ) --> ") ;
95128#endif
96129 switch ( _reader . TokenType )
97130 {
@@ -113,11 +146,14 @@ public void ReadBlock(Func<JsonReader, bool> tryParse)
113146 _path . Append ( ( string ) _reader . Value ) ;
114147 break ;
115148 case JsonToken . EndObject :
116- var end = _pathPositions . Pop ( ) ;
149+ _pathPositions . Pop ( ) ;
117150 _path . Length = _pathPositions . Pop ( ) ;
118- if ( end == basePosition ) return ;
119151 break ;
120152 }
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 ;
121157
122158 switch ( _reader . TokenType )
123159 {
@@ -130,11 +166,14 @@ public void ReadBlock(Func<JsonReader, bool> tryParse)
130166 case JsonToken . Integer :
131167 case JsonToken . Null :
132168 case JsonToken . String :
133- Path = _path . ToString ( ) . Substring ( basePosition ) ;
134- 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 ( ) ;
135172 break ;
136173 }
137174 }
175+
176+ TraceWarning ( $ "Unexpected end of file in { FullPath } ") ;
138177 }
139178
140179 public bool TryRead < T > ( Func < JsonReader , T > read , out T output )
@@ -153,7 +192,7 @@ public T AsEnum<T>(T defaultValue)
153192 var value = ( string ) _reader . Value ;
154193 return ( T ) Enum . Parse ( typeof ( T ) , value , true ) ;
155194 default :
156- TraceWarning ( $ "Expected string (enum) value in { Path } ; got { _reader . TokenType } ") ;
195+ TraceWarning ( $ "Expected string (enum) value in { FullPath } ; got { _reader . TokenType } ") ;
157196 return defaultValue ;
158197 }
159198 }
@@ -167,7 +206,7 @@ public float AsFloat(float defaultValue)
167206 case JsonToken . Integer :
168207 return ( long ) _reader . Value ;
169208 default :
170- TraceWarning ( $ "Expected floating point value in { Path } ; got { _reader . TokenType } ") ;
209+ TraceWarning ( $ "Expected floating point value in { FullPath } ; got { _reader . TokenType } ") ;
171210 return defaultValue ;
172211 }
173212 }
@@ -179,7 +218,7 @@ public int AsInteger(int defaultValue)
179218 case JsonToken . Integer :
180219 return ( int ) ( long ) _reader . Value ;
181220 default :
182- TraceWarning ( $ "Expected integer value in { Path } ; got { _reader . TokenType } ") ;
221+ TraceWarning ( $ "Expected integer value in { FullPath } ; got { _reader . TokenType } ") ;
183222 return defaultValue ;
184223 }
185224 }
@@ -191,7 +230,7 @@ public bool AsBoolean(bool defaultValue)
191230 case JsonToken . Boolean :
192231 return ( bool ) _reader . Value ;
193232 default :
194- TraceWarning ( $ "Expected Boolean value in { Path } ; got { _reader . TokenType } ") ;
233+ TraceWarning ( $ "Expected Boolean value in { FullPath } ; got { _reader . TokenType } ") ;
195234 return defaultValue ;
196235 }
197236 }
@@ -203,7 +242,7 @@ public string AsString(string defaultValue)
203242 case JsonToken . String :
204243 return ( string ) _reader . Value ;
205244 default :
206- TraceWarning ( $ "Expected string value in { Path } ; got { _reader . TokenType } ") ;
245+ TraceWarning ( $ "Expected string value in { FullPath } ; got { _reader . TokenType } ") ;
207246 return defaultValue ;
208247 }
209248 }
@@ -217,33 +256,33 @@ public float AsTime(float defaultValue)
217256 var StartTime = new TimeSpan ( int . Parse ( time [ 0 ] ) , time . Length > 1 ? int . Parse ( time [ 1 ] ) : 0 , time . Length > 2 ? int . Parse ( time [ 2 ] ) : 0 ) ;
218257 return ( float ) StartTime . TotalSeconds ;
219258 default :
220- TraceWarning ( $ "Expected string (time) value in { Path } ; got { _reader . TokenType } ") ;
259+ TraceWarning ( $ "Expected string (time) value in { FullPath } ; got { _reader . TokenType } ") ;
221260 return defaultValue ;
222261 }
223262 }
224263
225264 public Vector3 AsVector3 ( Vector3 defaultValue )
226265 {
227- var vector3 = defaultValue ;
228266 switch ( _reader . TokenType )
229267 {
230268 case JsonToken . StartArray :
231- if ( _reader . Read ( ) )
232- vector3 . X = AsFloat ( 0f ) ;
233- if ( _reader . Read ( ) )
234- vector3 . Y = AsFloat ( 0f ) ;
235- if ( _reader . Read ( ) )
236- vector3 . Z = AsFloat ( 0f ) ;
237- if ( ! _reader . Read ( ) || _reader . TokenType != JsonToken . EndArray )
238- goto default ; // We did not have exactly 3 items in the array
239- _path . Length = _pathPositions . Pop ( ) ;
240- return vector3 ;
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 ;
241284 default :
242- TraceWarning ( $ "Expected Vector3 (3 item array) in { Path } ; got { _reader . TokenType } ") ;
243-
244- // If the end of the array is not found in the right position, then parsing of subsequence objects also fails.
245- TraceWarning ( $ "Subsequent objects may be skipped") ;
246-
285+ TraceWarning ( $ "Expected array (Vector3) value in { FullPath } ; got { _reader . TokenType } ") ;
247286 return defaultValue ;
248287 }
249288 }
@@ -257,6 +296,7 @@ public void TraceWarning(string message)
257296 public void TraceInformation ( string message )
258297 {
259298 Trace . TraceInformation ( "{2} in {0}:line {1}" , _fileName , _reader . LineNumber , message ) ;
299+ _countInformations ++ ;
260300 }
261301 }
262302}
0 commit comments