From c6c32053d3e7b5e437332903183a4eff08f32dab Mon Sep 17 00:00:00 2001 From: Michael Rybakin Date: Thu, 23 May 2024 18:03:05 +0400 Subject: [PATCH] =?UTF-8?q?fix=20#1373,=20#1404=20=D0=B4=D0=BB=D1=8F=20v2:?= =?UTF-8?q?=20=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D0=B5=20JSON?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Json/GlobalJsonFunctions.cs | 227 ++++++----- .../Json/JSONReader.cs | 224 +++++------ tests/global-json.os | 362 ++++++++++++++++++ tests/json/json-mock.json | 2 - tests/json/test-json_reader.os | 93 ++++- 5 files changed, 692 insertions(+), 216 deletions(-) create mode 100644 tests/global-json.os diff --git a/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs b/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs index 939c2d02d..33433e531 100644 --- a/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs +++ b/src/OneScript.StandardLibrary/Json/GlobalJsonFunctions.cs @@ -6,6 +6,7 @@ This Source Code Form is subject to the terms of the ----------------------------------------------------------*/ using System; +using System.Collections.Generic; using Newtonsoft.Json; using OneScript.Commons; using OneScript.Contexts; @@ -59,130 +60,143 @@ 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() : jsonReader.Read(); } - public IValue ReadJSONInStruct(JSONReader Reader, bool nestedArray = false) + internal class JsonReaderInternal { - if (nestedArray) - { - ArrayImpl NestedArray = new ArrayImpl(); + private readonly JSONReader _reader; + private Func _builder; + private Action _inserter; - while (Reader.Read()) + private void Init() + { + 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() where T: IEnumerable { - if (Reader.CurrentJsonTokenType == JsonToken.PropertyName) + System.Diagnostics.Debug.Assert(typeof(T)==typeof(StructureImpl)||typeof(T)==typeof(MapImpl)); + Init(); + + 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) + { + 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; - } - 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.StartArray: + var resArray = new ArrayImpl(); + + while (ReadJsonValue(out value)) + { + resArray.Add(value); + } + + if (_reader.CurrentJsonTokenType == JsonToken.EndArray) + { + value = resArray; + return true; + } + break; + + 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; + } + + public IValue ReadJSONInMap(JSONReader reader) + { + var jsonReader = new JsonReaderInternal(reader); + return jsonReader.Read(); } /// @@ -214,17 +228,26 @@ public IValue ReadJSONDate(string String, JSONDateFormatEnum? format) break; case JSONDateFormatEnum.JavaScript: default: - throw new RuntimeException(Locale.NStr("ru='Формат даты JavaScript не поддерживается.'; en='JavaScript date format is not supported'")); + 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(json, settings); - return ValueFactory.Create((DateTime)result.Date); + }; + + try + { + var result = JsonConvert.DeserializeObject(json, settings); + return ValueFactory.Create((DateTime)result.Date); + } + catch (JsonException) + { + throw new RuntimeException(Locale.NStr("ru='Представление даты имеет неверный формат.'; en='Invalid date presentation format'")); + } } /// diff --git a/src/OneScript.StandardLibrary/Json/JSONReader.cs b/src/OneScript.StandardLibrary/Json/JSONReader.cs index 9eb28fedd..1935bedb2 100644 --- a/src/OneScript.StandardLibrary/Json/JSONReader.cs +++ b/src/OneScript.StandardLibrary/Json/JSONReader.cs @@ -12,11 +12,34 @@ This Source Code Form is subject to the terms of the using OneScript.Contexts; using OneScript.Exceptions; using OneScript.StandardLibrary.Text; +using ScriptEngine; using ScriptEngine.Machine; using ScriptEngine.Machine.Contexts; namespace OneScript.StandardLibrary.Json { + internal class JsonReaderInternal: JsonTextReader // из библиотеки Newtonsoft + { + public JsonReaderInternal(TextReader reader) : base(reader) + { + Finished = false; + } + + public override bool Read() + { + if (!base.Read()) + { + Finished = true; + return false; + } + if (TokenType != JsonToken.Undefined) + return true; + + throw JSONReaderException.UnexpectedSymbol(); + } + public bool Finished { get; private set; } + } + /// /// /// Предназначен для последовательного чтения JSON-данных из файла или строки. @@ -25,15 +48,17 @@ namespace OneScript.StandardLibrary.Json public class JSONReader : AutoContext { - private JsonTextReader _reader; // Объект из библиотеки Newtonsoft для работы с форматом JSON + private JsonReaderInternal _reader; /// /// /// Возвращает true если для объекта чтения json был задан текст для парсинга. /// - private bool IsOpen() + private bool IsOpen() => _reader != null; + + private void CheckIfOpen() { - return _reader != null; + if (_reader == null) throw JSONReaderException.NotOpen(); } public JSONReader() @@ -61,13 +86,11 @@ public IValue CurrentPosition { return ValueFactory.Create(_reader.LinePosition); } - else - return ValueFactory.Create(); // Неопределено - } + return ValueFactory.Create(); // Неопределено + } } - /// /// /// Указывает на позицию сразу после прочитанного значения. @@ -83,27 +106,23 @@ public IValue CurrentLine { return ValueFactory.Create(_reader.LineNumber); } - else - return ValueFactory.Create(); // Неопределено - } + return ValueFactory.Create(); // Неопределено + } } - /// /// /// Содержит текущее значение: /// /// - Число - если ТипТекущегоЗначения имеет значение Число; - /// - Строка - если ТипТекущегоЗначения имеет значение: - /// - /// - Комментарий, - /// - ИмяСвойства, - /// - Строка; + /// - Строка - если ТипТекущегоЗначения имеет одно из следующих значений: + /// - Комментарий, + /// - ИмяСвойства, + /// - Строка; /// - Булево - если ТипТекущегоЗначения имеет значение Булево, /// - Неопределено - если ТипТекущегоЗначения имеет значение Null. /// Исключение генерируется в случае, если ТипТекущегоЗначения имеет одно из следующих значений: - /// /// - НачалоМассива, /// - КонецМассива, /// - НачалоОбъекта, @@ -116,35 +135,34 @@ public IValue CurrentValue { get { - if (IsOpen()) + CheckIfOpen(); + if (_reader.Finished) + throw JSONReaderException.CannotGetValue(); + + switch (_reader.TokenType) { - switch (_reader.TokenType) - { - case JsonToken.String: - case JsonToken.Comment: - case JsonToken.PropertyName: - return ValueFactory.Create((string)_reader.Value); - - case JsonToken.Integer: - case JsonToken.Float: - return ValueFactory.Create(Convert.ToDecimal(_reader.Value)); - - case JsonToken.Boolean: - return ValueFactory.Create((bool)_reader.Value); - - case JsonToken.Date: - return ValueFactory.Create((DateTime)_reader.Value); - - case JsonToken.Null: - case JsonToken.Undefined: - return ValueFactory.Create(); - - default: - throw CannotGetValueException(); - } + case JsonToken.String: + case JsonToken.Comment: + case JsonToken.PropertyName: + return ValueFactory.Create((string)_reader.Value); + + case JsonToken.Integer: + case JsonToken.Float: + return ValueFactory.Create(Convert.ToDecimal(_reader.Value)); + + case JsonToken.Boolean: + return ValueFactory.Create((bool)_reader.Value); + + case JsonToken.Date: + return ValueFactory.Create((DateTime)_reader.Value); + + case JsonToken.Null: + return ValueFactory.Create(); + + default: + throw JSONReaderException.CannotGetValue(); + ; } - else - throw NotOpenException(); } } @@ -158,16 +176,11 @@ public JsonToken CurrentJsonTokenType { get { - if (IsOpen()) - { - return _reader.TokenType; - } - else - throw NotOpenException(); + CheckIfOpen(); + return _reader.TokenType; } } - /// /// /// Тип текущего значения в документе JSON. @@ -179,29 +192,27 @@ public JSONValueTypeEnum CurrentValueType { get { - if (IsOpen()) + CheckIfOpen(); + + if (_reader.Finished) + return JSONValueTypeEnum.None; + + return _reader.TokenType switch { - return _reader.TokenType switch - { - JsonToken.Null => JSONValueTypeEnum.Null, - JsonToken.Undefined => JSONValueTypeEnum.Null, - JsonToken.StartObject => JSONValueTypeEnum.ObjectStart, - JsonToken.StartArray => JSONValueTypeEnum.ArrayStart, - JsonToken.PropertyName => JSONValueTypeEnum.PropertyName, - JsonToken.Comment => JSONValueTypeEnum.Comment, - JsonToken.Integer => JSONValueTypeEnum.Number, - JsonToken.Float => JSONValueTypeEnum.Number, - JsonToken.String => JSONValueTypeEnum.String, - JsonToken.Boolean => JSONValueTypeEnum.Boolean, - JsonToken.EndObject => JSONValueTypeEnum.ObjectEnd, - JsonToken.EndArray => JSONValueTypeEnum.ArrayEnd, - _ => JSONValueTypeEnum.None - }; - } - else - throw NotOpenException(); + JsonToken.Null => JSONValueTypeEnum.Null, + JsonToken.StartObject => JSONValueTypeEnum.ObjectStart, + JsonToken.StartArray => JSONValueTypeEnum.ArrayStart, + JsonToken.PropertyName => JSONValueTypeEnum.PropertyName, + JsonToken.Comment => JSONValueTypeEnum.Comment, + JsonToken.Integer => JSONValueTypeEnum.Number, + JsonToken.Float => JSONValueTypeEnum.Number, + JsonToken.String => JSONValueTypeEnum.String, + JsonToken.Boolean => JSONValueTypeEnum.Boolean, + JsonToken.EndObject => JSONValueTypeEnum.ObjectEnd, + JsonToken.EndArray => JSONValueTypeEnum.ArrayEnd, + _ => JSONValueTypeEnum.None + }; } - } /// @@ -209,12 +220,9 @@ public JSONValueTypeEnum CurrentValueType /// Завершает чтение текста JSON из файла или строки. /// /// - /// - /// [ContextMethod("Закрыть", "Close")] public void Close() { - if (_reader != null) { _reader.Close(); @@ -222,7 +230,6 @@ public void Close() } } - /// /// /// Открывает JSON-файл для чтения данным объектом. Если перед вызовом данного метода уже производилось чтение JSON из другого файла или строки, то чтение прекращается и объект инициализируется для чтения из указанного файла. @@ -235,7 +242,6 @@ public void Close() [ContextMethod("ОткрытьФайл", "OpenFile")] public void OpenFile(string JSONFileName, IValue encoding = null) { - if (IsOpen()) Close(); @@ -253,11 +259,12 @@ public void OpenFile(string JSONFileName, IValue encoding = null) throw new RuntimeException(e.Message, e); } - _reader = new JsonTextReader(_fileReader); - + _reader = new JsonReaderInternal(_fileReader) + { + SupportMultipleContent = true + }; } - /// /// Если текущее значение – начало массива или объекта, то пропускает его содержимое и конец. /// Для остальных типов значений работает аналогично методу Прочитать(). @@ -266,35 +273,16 @@ public void OpenFile(string JSONFileName, IValue encoding = null) [ContextMethod("Пропустить", "Skip")] public bool Skip() { + CheckIfOpen(); - if (IsOpen()) + if (_reader.TokenType == JsonToken.StartArray || _reader.TokenType == JsonToken.StartObject) { - if (_reader.TokenType == JsonToken.StartArray || _reader.TokenType == JsonToken.StartObject) - { - while (_reader.Read()) - { - if (_reader.TokenType == JsonToken.EndArray || _reader.TokenType == JsonToken.EndObject) - { - return _reader.Read(); - } - } - return true; - } - else - { - if (_reader.Read()) - return _reader.Read(); - else - return false; - } - + _reader.Skip(); } - else - throw NotOpenException(); + return _reader.Read(); } - /// /// Выполняет чтение значения JSON. /// @@ -302,10 +290,10 @@ public bool Skip() [ContextMethod("Прочитать", "Read")] public bool Read() { + CheckIfOpen(); return _reader.Read(); - - } + } /// /// @@ -322,20 +310,36 @@ public void SetString(string JSONString) if (IsOpen()) Close(); - _reader = new JsonTextReader(new StringReader(JSONString)); + _reader = new JsonReaderInternal(new StringReader(JSONString)) + { + SupportMultipleContent = true + }; } - RuntimeException NotOpenException() + } + + public class JSONReaderException : RuntimeException + { + public JSONReaderException(string message) : base(message) + { + } + + public static JSONReaderException NotOpen() { - return new RuntimeException(Locale.NStr + return new JSONReaderException(Locale.NStr ("ru='Источник данных JSON не открыт'; en='JSON data source is not opened'")); } - RuntimeException CannotGetValueException() + public static JSONReaderException CannotGetValue() { - return new RuntimeException(Locale.NStr + return new JSONReaderException(Locale.NStr ("ru='Текущее значение JSON не может быть получено';en='Cannot get current JSON value'")); } + public static JSONReaderException UnexpectedSymbol() + { + return new JSONReaderException(Locale.NStr + ("ru='Непредвиденный символ при чтении JSON';en='Unexpected symbol during JSON reading'")); + } } } diff --git a/tests/global-json.os b/tests/global-json.os new file mode 100644 index 000000000..506df914e --- /dev/null +++ b/tests/global-json.os @@ -0,0 +1,362 @@ +Перем юТест; + +Функция ПолучитьСписокТестов(Тестирование) Экспорт + + юТест = Тестирование; + + СписокТестов = Новый Массив; + + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеСтруктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеСоответствия"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеЧисла"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеСтроки"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеПустогоМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеПустойСтруктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеПустогоСоответствия"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПолучениеВложенныхМассивов"); + + // issue #1373 + СписокТестов.Добавить("Тест_ДолженПроверить_ОднострочныйКомментарийВКонцеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ОднострочныйКомментарийВнутриСтуктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ОднострочныйКомментарийВНачалеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_МногострочныйКомментарийВнутриСтуктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_МногострочныйКомментарийВнутриМассива"); + + СписокТестов.Добавить("Тест_ДолженПроверить_ПродолжениеЧтения"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПродолжениеЧтенияСИзменениемСтруктуры"); + + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ДляUndefined"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомМассиве"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомОбъекте"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномМассиве"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномОбъекте"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПропущенномЗначенииСвойства"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНезакрытомКомментарии"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриНедопустимомКлючеСтруктуры"); + СписокТестов.Добавить("Тест_ДолженПроверить_ПроизвольныйКлючСоответствия"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПустомЗначенииВнутриМассива"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПустомЗначенииВКонцеМассива"); + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ПриПустомЗначенииСвойства"); + СписокТестов.Добавить("Тест_ДолженПроверить_ЗапятуюВКонцеМассива"); + СписокТестов.Добавить("Тест_ДолженПроверить_ЗапятуюВКонцеОбъекта"); + + Возврат СписокТестов; + +КонецФункции + +Функция ПрочитатьJSONИзСтроки(Стр, Знач ВСоответствие=Ложь) + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Стр); + Результат = ПрочитатьJSON(Чтение, ВСоответствие); + Чтение.Закрыть(); + Возврат Результат; +КонецФункции + +Процедура Тест_ДолженПроверить_ПолучениеМассива() Экспорт + Текст = "[123,456,789]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(3, Результат.Количество()); + юТест.ПроверитьРавенство(456, Результат[1]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеСтруктуры() Экспорт + Текст = "{""Поле1"":123,""Поле2"":456}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат) ); + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство(456, Результат.Поле2); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеСоответствия() Экспорт + Текст = "{""Поле1"":123,""Поле2"":456}"; + Результат = ПрочитатьJSONИзСтроки(Текст,Истина); + + юТест.ПроверитьРавенство(Тип("Соответствие"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство(456, Результат["Поле2"]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеЧисла() Экспорт + Текст = "321"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Число"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(321, Результат); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеСтроки() Экспорт + Текст = """абв"""; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(ТипЗнч(Результат), Тип("Строка")); + юТест.ПроверитьРавенство(Результат, "абв"); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеПустогоМассива() Экспорт + Текст = "[]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат) ); + юТест.ПроверитьРавенство(0, Результат.Количество()); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеПустойСтруктуры() Экспорт + Текст = "{}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(Результат.Количество(), 0); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеПустогоСоответствия() Экспорт + Текст = "{}"; + Результат = ПрочитатьJSONИзСтроки(Текст,Истина); + + юТест.ПроверитьРавенство(Тип("Соответствие"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(Результат.Количество(), 0); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПолучениеВложенныхМассивов() Экспорт + Текст = "[[123,456],789,[-3,-2,""-0""],{""Ф"":[""Ы"",222]}]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат)); + юТест.ПроверитьРавенство(4, Результат.Количество()); + юТест.ПроверитьРавенство(Тип("Число"), ТипЗнч(Результат[1])); + юТест.ПроверитьРавенство(789, Результат[1]); + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат[2])); + юТест.ПроверитьРавенство(3, Результат[2].Количество()); + юТест.ПроверитьРавенство("-0", Результат[2][2]); + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат[3])); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ОднострочныйКомментарийВКонцеМассива() Экспорт + Текст = "[ + |{ + | ""NAME"": ""n1"", + | ""Method"": ""m1"", + | ""PATH"": ""p1"" + |}, + |{ + | ""NAME"": ""n2"", + | //""Method"": ""m2"", + | ""PATH"": ""p2"" + |} + |//}, + |//{ + |// ""NAME"": """", + |]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство("p2", Результат[1].PATH); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ОднострочныйКомментарийВнутриСтуктуры() Экспорт + Текст = " + |{ + | ""test"": //fail + | ""pass"" + |}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("pass", Результат.test); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ОднострочныйКомментарийВНачалеМассива() Экспорт + Текст = " + |[// !!! + |""fail"",""pass""] + |"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("pass", Результат[1]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_МногострочныйКомментарийВнутриСтуктуры() Экспорт + Текст = " + |{ + | ""test"": /*fail + | ""??""*/ ""ok"" + |}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("ok", Результат.test); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_МногострочныйКомментарийВнутриМассива() Экспорт + Текст = " + |[ + | ""test"", /*fail + | ""??""]*/ ""ok"" + |]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство("ok", Результат[1]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПродолжениеЧтения() Экспорт + Текст = "[123,456] {""аб"":""вг"",""де"":""жз"",""ик"":""лм""}"; + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Результат1 = ПрочитатьJSON(Чтение); + Результат2 = ПрочитатьJSON(Чтение); + Чтение.Закрыть(); + + юТест.ПроверитьРавенство(Тип("Массив"), ТипЗнч(Результат1)); + юТест.ПроверитьРавенство(2, Результат1.Количество()); + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат2)); + юТест.ПроверитьРавенство(3, Результат2.Количество()); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПродолжениеЧтенияСИзменениемСтруктуры() Экспорт + Текст = "{""аб"":""вг"",""де"":""жз""} {""ик"":""лм""}"; + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Результат1 = ПрочитатьJSON(Чтение); + Результат2 = ПрочитатьJSON(Чтение,Истина); + Чтение.Закрыть(); + + юТест.ПроверитьРавенство(Тип("Структура"), ТипЗнч(Результат1)); + юТест.ПроверитьРавенство(2, Результат1.Количество()); + юТест.ПроверитьРавенство(Тип("Соответствие"), ТипЗнч(Результат2)); + юТест.ПроверитьРавенство(1, Результат2.Количество()); + юТест.ПроверитьРавенство("лм", Результат2["ик"]); +КонецПроцедуры + + +Процедура Тест_ДолженВызватьОшибку_ДляUndefined() Экспорт + Текст = "[123,undefined,456]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомМассиве() Экспорт + Текст = "[[[123,456]]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомОбъекте() Экспорт + Текст = "{""aбв"":456"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномМассиве() Экспорт + Текст = "{""aбв"":[456,789}"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомВложенномОбъекте() Экспорт + Текст = "[{""aбв"":456]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПропущенномЗначенииСвойства() Экспорт + Текст = "{""aбв"":}"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНезакрытомКомментарии() Экспорт + Текст = "[123,456 /*]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриНедопустимомКлючеСтруктуры() Экспорт + Текст = "{""123"":""456""}"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ПроизвольныйКлючСоответствия() Экспорт + Текст = "{""%/$!"":""456""}"; + Результат = ПрочитатьJSONИзСтроки(Текст, Истина); + юТест.ПроверитьРавенство("456", Результат["%/$!"]); +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПустомЗначенииВнутриМассива() Экспорт + Текст = "[123,,456]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПустомЗначенииВКонцеМассива() Экспорт + Текст = "[123,456,,]"; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженВызватьОшибку_ПриПустомЗначенииСвойства() Экспорт + Текст = "{""аб"": , ""еж"":""кл""}";; + Попытка + Результат = ПрочитатьJSONИзСтроки(Текст); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ЗапятуюВКонцеМассива() Экспорт + Текст = "[123,456,789,]"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(3, Результат.Количество()); + юТест.ПроверитьРавенство(789, Результат[2]); +КонецПроцедуры + +Процедура Тест_ДолженПроверить_ЗапятуюВКонцеОбъекта() Экспорт + Текст = "{""аб"":""гд"",""еж"":""кл"",}"; + Результат = ПрочитатьJSONИзСтроки(Текст); + + юТест.ПроверитьРавенство(2, Результат.Количество()); + юТест.ПроверитьРавенство("кл", Результат["еж"]); +КонецПроцедуры diff --git a/tests/json/json-mock.json b/tests/json/json-mock.json index 519215a78..c570d1e8e 100644 --- a/tests/json/json-mock.json +++ b/tests/json/json-mock.json @@ -1,6 +1,5 @@ { "Null": null, - "Неопределено": undefined, "Ложь": false, "Истина": true, "Число (плавающая точка)": 1.001e-2, @@ -17,7 +16,6 @@ "Пустой объект": {}, "Массив": [ null, - undefined, false, true, 1.001e-2, diff --git a/tests/json/test-json_reader.os b/tests/json/test-json_reader.os index 1d1487290..a3e98504a 100644 --- a/tests/json/test-json_reader.os +++ b/tests/json/test-json_reader.os @@ -16,6 +16,14 @@ СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSON"); СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONВСтруктуру"); СписокТестов.Добавить("Тест_Должен_СверитьХешСуммуРезультатаПарсингаJSONМассива"); + + СписокТестов.Добавить("Тест_ДолженВызватьОшибку_ДляUndefined"); + + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускЗначения"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассива"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускНезавершенногоМассива"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускМассиваСВложенными"); + СписокТестов.Добавить("Тест_Должен_ПроверитьПропускОбъектаСВложениями"); Возврат СписокТестов; КонецФункции @@ -57,7 +65,7 @@ СтруктураДанных = ПолучитьСтруктуруДанных("json/json-mock.json", Истина); - юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 4099998885); + юТест.ПроверитьРавенство(РассчитатьХешСумму(СтруктураДанных), 960829385); КонецПроцедуры @@ -129,4 +137,85 @@ Возврат Результат; -КонецФункции \ No newline at end of file +КонецФункции + +Процедура Тест_ДолженВызватьОшибку_ДляUndefined() Экспорт + Текст = "undefined"; + Чтение = Новый ЧтениеJSON; + Чтение.УстановитьСтроку(Текст); + Попытка + Результат = Чтение.Прочитать(); + Исключение + Возврат; + КонецПопытки; + ВызватьИсключение "Должно было быть выдано исключение, но его не было"; +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускЗначения() Экспорт + Текст = "1,2,3,4,5"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); + Результат = Чтение.Пропустить(); + + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(2, Чтение.ТекущееЗначение); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускМассива() Экспорт + Текст = "1,[2,3],4,5"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); // считывает 1 + Чтение.Прочитать(); // считывает [ + Результат = Чтение.Пропустить(); + + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(4, Чтение.ТекущееЗначение); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускНезавершенногоМассива() Экспорт + Текст = "1,[2,"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); + Чтение.Прочитать(); + Результат = Чтение.Пропустить(); + + юТест.ПроверитьРавенство(Ложь, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Ничего, Чтение.ТипТекущегоЗначения); +КонецПроцедуры + + +Процедура Тест_Должен_ПроверитьПропускМассиваСВложенными() Экспорт + Текст = "1,[2,[-1,-2],[-3,-4],3],4,5"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); // считывает 1 + Чтение.Прочитать(); // считывает НачалоМассива + Результат = Чтение.Пропустить(); // должно пропустить масиив от '[2,' до ',3]' и считать 4 + + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(4, Чтение.ТекущееЗначение); +КонецПроцедуры + +Процедура Тест_Должен_ПроверитьПропускОбъектаСВложениями() Экспорт + Текст = "1, {""a"":[2,[-1,-2],[-3,-4],3], ""b"":{ ""C"":""D""} }, 4, 5, {}"; + + Чтение = Новый ЧтениеJSON(); + Чтение.УстановитьСтроку(Текст); + Чтение.Прочитать(); + Чтение.Прочитать(); + Чтение.Пропустить(); + Результат = Чтение.Прочитать(); + юТест.ПроверитьРавенство(Истина, Результат); + юТест.ПроверитьРавенство(ТипЗначенияJSON.Число, Чтение.ТипТекущегоЗначения); + юТест.ПроверитьРавенство(5, Чтение.ТекущееЗначение); +КонецПроцедуры