Skip to content

Commit 9e77345

Browse files
committed
fix: JsonReader.AsVector3 improvements and tests
1 parent 4e83555 commit 9e77345

File tree

3 files changed

+362
-36
lines changed

3 files changed

+362
-36
lines changed

Source/Orts.Parsers.OR/JsonReader.cs

Lines changed: 76 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
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

Comments
 (0)