-
Notifications
You must be signed in to change notification settings - Fork 107
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Значительные исправления ПрочитатьJSON (fix #1373 и прочее) #1374
Changes from all commits
6bc8225
34877a8
f0743df
a9195f7
4165865
2ce5f3a
fccaa14
8289191
0a6db86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ This Source Code Form is subject to the terms of the | |
using ScriptEngine.Machine.Contexts; | ||
using Newtonsoft.Json; | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace ScriptEngine.HostedScript.Library.Json | ||
{ | ||
|
@@ -55,132 +56,140 @@ public static IAttachableContext CreateInstance() | |
[ContextMethod("ПрочитатьJSON", "ReadJSON")] | ||
public IValue ReadJSON(JSONReader Reader, bool ReadToMap = false, IValue PropertiesWithDateValuesNames = null, IValue ExpectedDateFormat = null, string ReviverFunctionName = null, IValue ReviverFunctionModule = null, IValue ReviverFunctionAdditionalParameters = null, IValue RetriverPropertiesNames = null, int MaximumNesting = 500) | ||
{ | ||
if (ReadToMap) | ||
return ReadJSONInMap(Reader); | ||
else | ||
return ReadJSONInStruct(Reader); | ||
var jsonReader = new JsonReaderInternal(Reader); | ||
return ReadToMap ? jsonReader.Read<MapImpl>() : jsonReader.Read<StructureImpl>(); | ||
} | ||
|
||
public IValue ReadJSONInStruct(JSONReader Reader, bool nestedArray = false) | ||
internal class JsonReaderInternal | ||
{ | ||
if (nestedArray) | ||
{ | ||
ArrayImpl NestedArray = new ArrayImpl(); | ||
private readonly JSONReader _reader; | ||
private Func<IValue> _builder; | ||
private Action<IValue, string, IValue> _inserter; | ||
|
||
while (Reader.Read()) | ||
private void Init<TStructure>() | ||
{ | ||
if (typeof(TStructure) == typeof(StructureImpl)) | ||
{ | ||
if (Reader.CurrentJsonTokenType == JsonToken.EndArray) | ||
{ | ||
break; | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartObject) | ||
{ | ||
NestedArray.Add(ReadJSONInStruct(Reader)); | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) | ||
{ | ||
NestedArray.Add(ReadJSONInStruct(Reader, true)); | ||
} | ||
else | ||
NestedArray.Add(Reader.CurrentValue); | ||
_builder = () => new StructureImpl(); | ||
_inserter = (o, s, v) => ((StructureImpl)o).Insert(s, v); | ||
} | ||
else if (typeof(TStructure) == typeof(MapImpl)) | ||
{ | ||
_builder = () => new MapImpl(); | ||
_inserter = (o, s, v) =>((MapImpl)o).Insert(ValueFactory.Create(s), v); | ||
} | ||
return NestedArray; | ||
else | ||
{ | ||
throw new InvalidOperationException(); | ||
} | ||
} | ||
|
||
public JsonReaderInternal(JSONReader reader) | ||
{ | ||
_reader = reader; | ||
} | ||
|
||
StructureImpl ResStruct = new StructureImpl(); | ||
private IValue Create() => _builder(); | ||
|
||
while ((Reader).Read()) | ||
private void AddProperty(IValue obj, string str, IValue val) => _inserter(obj, str, val); | ||
|
||
public IValue Read<T>() where T: IEnumerable<KeyAndValueImpl> | ||
{ | ||
if (Reader.CurrentJsonTokenType == JsonToken.PropertyName) | ||
System.Diagnostics.Debug.Assert(typeof(T)==typeof(StructureImpl)||typeof(T)==typeof(MapImpl)); | ||
Init<T>(); | ||
|
||
try | ||
{ | ||
string PropertyName = Reader.CurrentValue.AsString(); | ||
Reader.Read(); | ||
|
||
if (Reader.CurrentJsonTokenType == JsonToken.StartObject) | ||
{ | ||
ResStruct.Insert(PropertyName, ReadJSONInStruct(Reader)); | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) | ||
{ | ||
ResStruct.Insert(PropertyName, ReadJSONInStruct(Reader, true)); | ||
} | ||
else | ||
{ | ||
ResStruct.Insert(PropertyName, Reader.CurrentValue); | ||
} | ||
if (ReadJsonValue(out var value)) | ||
return value; | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.EndObject) | ||
catch (JSONReaderException) | ||
{ | ||
break; | ||
throw; | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) | ||
catch (Exception exc) | ||
{ | ||
return ReadJSONInStruct(Reader, true); | ||
throw InvalidJsonException(exc.Message); | ||
} | ||
} | ||
return ResStruct; | ||
} | ||
|
||
public IValue ReadJSONInMap(JSONReader Reader, bool nestedArray = false) | ||
{ | ||
throw InvalidJsonException(); | ||
} | ||
|
||
if (nestedArray) | ||
private JsonToken ReadJsonToken() | ||
{ | ||
ArrayImpl NestedArray = new ArrayImpl(); | ||
while (_reader.Read()) | ||
{ | ||
var tok = _reader.CurrentJsonTokenType; | ||
if (tok != JsonToken.Comment) | ||
return tok; | ||
} | ||
|
||
while (Reader.Read()) | ||
return JsonToken.None; | ||
} | ||
|
||
private bool ReadJsonValue(out IValue value) | ||
{ | ||
switch (ReadJsonToken()) | ||
{ | ||
if (Reader.CurrentJsonTokenType == JsonToken.EndArray) | ||
{ | ||
case JsonToken.StartObject: | ||
var jsonObject = Create(); | ||
|
||
while (ReadJsonToken() == JsonToken.PropertyName) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Если встретим комментарий, то ReadJsonToken вернет None и цикл чтения прервется. Например, {
"Свойство1": 1,
// пояснение для свойства 2
"Свойство2": "Значение"
} После считывания 1 мы найдем комментарий, вернем Nonе и цикл закончится, не дойдя до чтения "Свойство2", нет? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Нет, |
||
{ | ||
var propertyName = _reader.CurrentValue.AsString(); | ||
if (!ReadJsonValue(out value)) | ||
return false; | ||
|
||
AddProperty(jsonObject, propertyName, value); | ||
} | ||
|
||
if (_reader.CurrentJsonTokenType == JsonToken.EndObject) | ||
{ | ||
value = jsonObject; | ||
return true; | ||
} | ||
break; | ||
|
||
case JsonToken.StartArray: | ||
var resArray = new ArrayImpl(); | ||
|
||
while (ReadJsonValue(out value)) | ||
{ | ||
resArray.Add(value); | ||
} | ||
|
||
if (_reader.CurrentJsonTokenType == JsonToken.EndArray) | ||
{ | ||
value = resArray; | ||
return true; | ||
} | ||
break; | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartObject) | ||
{ | ||
NestedArray.Add(ReadJSONInMap(Reader)); | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) | ||
{ | ||
NestedArray.Add(ReadJSONInMap(Reader, true)); | ||
} | ||
else | ||
NestedArray.Add(Reader.CurrentValue); | ||
|
||
case JsonToken.EndArray: | ||
case JsonToken.EndObject: | ||
case JsonToken.None: | ||
break; | ||
|
||
default: | ||
value = _reader.CurrentValue; | ||
return true; | ||
} | ||
return NestedArray; | ||
|
||
value = null; | ||
return false; | ||
} | ||
|
||
MapImpl ResStruct = new MapImpl(); | ||
while ((Reader).Read()) | ||
private RuntimeException InvalidJsonException(string message=null) | ||
{ | ||
if (Reader.CurrentJsonTokenType == JsonToken.PropertyName) | ||
{ | ||
string PropertyName = Reader.CurrentValue.AsString(); | ||
Reader.Read(); | ||
|
||
if (Reader.CurrentJsonTokenType == JsonToken.StartObject) | ||
{ | ||
ResStruct.Insert(ValueFactory.Create(PropertyName), ReadJSONInMap(Reader)); | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) | ||
{ | ||
ResStruct.Insert(ValueFactory.Create(PropertyName), ReadJSONInMap(Reader, true)); | ||
} | ||
else | ||
{ | ||
ResStruct.Insert(ValueFactory.Create(PropertyName), Reader.CurrentValue); | ||
} | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.EndObject) | ||
{ | ||
break; | ||
} | ||
else if (Reader.CurrentJsonTokenType == JsonToken.StartArray) | ||
{ | ||
return ReadJSONInMap(Reader, true); | ||
} | ||
var addition = string.IsNullOrWhiteSpace(message) ? string.Empty : $"\n({message})"; | ||
return new RuntimeException(string.Format(Locale.NStr | ||
("ru='Недопустимое состояние потока чтения JSON в строке {0} позиции {1}{2}'" | ||
+ "en='Invalid JSON reader state at line {0} position {1}{2}'"), | ||
_reader.CurrentLine, _reader.CurrentPosition, addition)); | ||
} | ||
return ResStruct; | ||
} | ||
|
||
|
||
/// <summary> | ||
/// | ||
/// Выполняет преобразование строки, прочитанной в JSON-формате, в значение типа Дата. | ||
|
@@ -197,7 +206,7 @@ public IValue ReadJSONInMap(JSONReader Reader, bool nestedArray = false) | |
[ContextMethod("ПрочитатьДатуJSON", "ReadJSONDate")] | ||
public IValue ReadJSONDate(string String, IValue Format) | ||
{ | ||
|
||
var format = Format.GetRawValue() as SelfAwareEnumValue<JSONDateFormatEnum>; | ||
var JSONDateFormatEnum = GlobalsManager.GetEnum<JSONDateFormatEnum>(); | ||
DateFormatHandling dateFormatHandling = new DateFormatHandling(); | ||
|
@@ -209,14 +218,22 @@ public IValue ReadJSONDate(string String, IValue Format) | |
else | ||
throw new RuntimeException(Locale.NStr("ru='Формат даты JavaScript не поддерживается.'; en='JavaScript date format is not supported'")); | ||
|
||
string json = @"{""Date"":""" + String + @"""}"; | ||
string json = @"{""Date"":""" + String + @"""}"; | ||
|
||
var settings = new JsonSerializerSettings | ||
{ | ||
DateFormatHandling = dateFormatHandling | ||
}; | ||
var result = JsonConvert.DeserializeObject<ConvertedDate>(json, settings); | ||
return ValueFactory.Create((DateTime)result.Date); | ||
}; | ||
|
||
try | ||
{ | ||
var result = JsonConvert.DeserializeObject<ConvertedDate>(json, settings); | ||
return ValueFactory.Create((DateTime)result.Date); | ||
} | ||
catch (JsonException) | ||
{ | ||
throw new RuntimeException(Locale.NStr("ru='Представление даты имеет неверный формат.'; en='Invalid date presentation format'")); | ||
} | ||
} | ||
|
||
/// <summary> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Потеряли исходное исключение. Вероятно, лучше добавить cause, чтобы не терять стек и описание причины.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Сделано намеренно, в исключении из Newtonsoft.JSON кроме позиции будет более подробное описание причины, но английское. Можно восстановить и вставить в сообщение.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А чем InnerException не подходит?