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//
1818// Use this define to diagnose issues in the JSON reader below.
1919//#define DEBUG_JSON_READER
2020
21+ using Microsoft . Xna . Framework ;
22+ using Newtonsoft . Json ;
2123using System ;
2224using System . Collections . Generic ;
2325using System . Diagnostics ;
2426using System . IO ;
2527using System . Linq ;
2628using System . Text ;
27- using Newtonsoft . Json ;
2829
2930namespace Orts . Parsers . OR
3031{
@@ -37,26 +38,44 @@ public class JsonReader
3738 /// <param name="tryParse"></param>
3839 public static void ReadFile ( string fileName , Func < JsonReader , bool > tryParse )
3940 {
40- using ( var reader = new JsonTextReader ( File . OpenText ( fileName ) )
41+ using ( var reader = new JsonTextReader ( File . OpenText ( fileName ) ) )
4142 {
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 ) ) )
4456 {
45- new JsonReader ( fileName , reader ) . ReadBlock ( tryParse ) ;
57+ var json = new JsonReader ( fileName , reader ) ;
58+ json . ReadFile ( tryParse ) ;
59+ return ( json . _countWarnings , json . _countInformations ) ;
4660 }
4761 }
4862
4963 string _fileName ;
5064 JsonTextReader _reader ;
5165 StringBuilder _path ;
5266 Stack < int > _pathPositions ;
67+ Stack < string > _paths ;
68+ int _countWarnings ;
69+ int _countInformations ;
70+
71+ string FullPath { get => _path . Length > 0 ? _path . ToString ( ) : "(root)" ; }
5372
5473 /// <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:
5675 /// JsonReader item;
5776 /// item.Path = "Changes[].Type"
5877 /// </summary>
59- public string Path { get ; private set ; }
78+ public string Path { get => _paths . Peek ( ) ; }
6079
6180 /// <summary>
6281 /// Note the values needed for parsing and helpful error messages
@@ -69,21 +88,43 @@ public static void ReadFile(string fileName, Func<JsonReader, bool> tryParse)
6988 _reader = reader ;
7089 _path = new StringBuilder ( ) ;
7190 _pathPositions = new Stack < int > ( ) ;
91+ _paths = new Stack < string > ( ) ;
7292 }
7393
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>
74116 public void ReadBlock ( Func < JsonReader , bool > tryParse )
75117 {
76118 var basePosition = _pathPositions . Count > 0 ? _pathPositions . Peek ( ) : 0 ;
77119
78120#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()") ;
81122#endif
82123
83124 while ( _reader . Read ( ) ) // Reads the next JSON token. Returns false if at end
84125 {
85126#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 ) } ) --> ") ;
87128#endif
88129 switch ( _reader . TokenType )
89130 {
@@ -105,27 +146,41 @@ public void ReadBlock(Func<JsonReader, bool> tryParse)
105146 _path . Append ( ( string ) _reader . Value ) ;
106147 break ;
107148 case JsonToken . EndObject :
108- var end = _pathPositions . Pop ( ) ;
149+ _pathPositions . Pop ( ) ;
109150 _path . Length = _pathPositions . Pop ( ) ;
110- if ( end == basePosition ) return ;
111151 break ;
112152 }
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 ;
113157
114158 switch ( _reader . TokenType )
115159 {
116160 case JsonToken . StartObject :
161+ case JsonToken . StartArray :
117162 case JsonToken . Boolean :
118163 case JsonToken . Bytes :
119164 case JsonToken . Date :
120165 case JsonToken . Float :
121166 case JsonToken . Integer :
122167 case JsonToken . Null :
123168 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 ( ) ;
126172 break ;
127173 }
128174 }
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 ;
129184 }
130185
131186 public T AsEnum < T > ( T defaultValue )
@@ -137,7 +192,7 @@ public T AsEnum<T>(T defaultValue)
137192 var value = ( string ) _reader . Value ;
138193 return ( T ) Enum . Parse ( typeof ( T ) , value , true ) ;
139194 default :
140- TraceWarning ( $ "Expected string (enum) value in { Path } ; got { _reader . TokenType } ") ;
195+ TraceWarning ( $ "Expected string (enum) value in { FullPath } ; got { _reader . TokenType } ") ;
141196 return defaultValue ;
142197 }
143198 }
@@ -151,7 +206,7 @@ public float AsFloat(float defaultValue)
151206 case JsonToken . Integer :
152207 return ( long ) _reader . Value ;
153208 default :
154- TraceWarning ( $ "Expected floating point value in { Path } ; got { _reader . TokenType } ") ;
209+ TraceWarning ( $ "Expected floating point value in { FullPath } ; got { _reader . TokenType } ") ;
155210 return defaultValue ;
156211 }
157212 }
@@ -163,7 +218,19 @@ public int AsInteger(int defaultValue)
163218 case JsonToken . Integer :
164219 return ( int ) ( long ) _reader . Value ;
165220 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 } ") ;
167234 return defaultValue ;
168235 }
169236 }
@@ -175,7 +242,7 @@ public string AsString(string defaultValue)
175242 case JsonToken . String :
176243 return ( string ) _reader . Value ;
177244 default :
178- TraceWarning ( $ "Expected string value in { Path } ; got { _reader . TokenType } ") ;
245+ TraceWarning ( $ "Expected string value in { FullPath } ; got { _reader . TokenType } ") ;
179246 return defaultValue ;
180247 }
181248 }
@@ -189,19 +256,47 @@ public float AsTime(float defaultValue)
189256 var StartTime = new TimeSpan ( int . Parse ( time [ 0 ] ) , time . Length > 1 ? int . Parse ( time [ 1 ] ) : 0 , time . Length > 2 ? int . Parse ( time [ 2 ] ) : 0 ) ;
190257 return ( float ) StartTime . TotalSeconds ;
191258 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 } ") ;
193286 return defaultValue ;
194287 }
195288 }
196289
197290 public void TraceWarning ( string message )
198291 {
199292 Trace . TraceWarning ( "{2} in {0}:line {1}" , _fileName , _reader . LineNumber , message ) ;
293+ _countWarnings ++ ;
200294 }
201295
202296 public void TraceInformation ( string message )
203297 {
204298 Trace . TraceInformation ( "{2} in {0}:line {1}" , _fileName , _reader . LineNumber , message ) ;
299+ _countInformations ++ ;
205300 }
206301 }
207302}
0 commit comments