Skip to content

Commit 60109ba

Browse files
authored
Merge pull request #648 from cjakeman/json-reader-vector3
adds AsBoolean, AsVector3 to JsonReader
2 parents 7c98da1 + 9e77345 commit 60109ba

File tree

4 files changed

+405
-20
lines changed

4 files changed

+405
-20
lines changed

Source/Orts.Parsers.OR/JsonReader.cs

Lines changed: 115 additions & 20 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
//
@@ -18,13 +18,14 @@
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;
2123
using System;
2224
using System.Collections.Generic;
2325
using System.Diagnostics;
2426
using System.IO;
2527
using System.Linq;
2628
using System.Text;
27-
using Newtonsoft.Json;
2829

2930
namespace 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
}

Source/Orts.Parsers.OR/Orts.Parsers.OR.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
<LangVersion>7.3</LangVersion>
3838
</PropertyGroup>
3939
<ItemGroup>
40+
<Reference Include="MonoGame.Framework, Version=3.7.1.189, Culture=neutral, processorArchitecture=MSIL">
41+
<SpecificVersion>False</SpecificVersion>
42+
<HintPath>..\3rdPartyLibs\MonoGame\MonoGame.Framework.dll</HintPath>
43+
</Reference>
4044
<Reference Include="Newtonsoft.Json, Version=4.5.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
4145
<SpecificVersion>False</SpecificVersion>
4246
<HintPath>..\3rdPartyLibs\Newtonsoft.Json.dll</HintPath>

0 commit comments

Comments
 (0)