предисловие
Когда json будет популярен и будет использоваться в качестве одного из основных форматов данных для внешнего и внутреннего взаимодействия, сколько людей будут иметь некоторые представления об использовании и понимании самого json и, конечно же, о преобразовании объектов json и их строковые формы будут следовать. В давние времена передняя часть использовала eval с низким энергопотреблением для преобразования формата.
Затем, исходя из здравого смысла, мы знаем, что JSON предоставляет два общих инструмента и метода для достижения взаимного преобразования, а именно JSON.parse() и JSON.stringfy(); с другой стороны, мы также знаем, что в целом мы имеем дело с объекты, возвращаемые серверной частью, имеют стандартный формат пары ключ-значение, например:{code:200,message:'success',data:{page:1,list:[]}}
Так что же происходит, когда мы конвертируем другие типы значений в бэкенде или в других сценариях? Эта идея возникла потому, что при обработке дела обнаружилось, что в бэкенде есть поле, а значение поля списка картинок возвращает '[url1, url2]', что, очевидно, является результатом строки массива. Сначала я не думал об использовании метода разбора, потому что это не были данные json в моем сознании.
что такое json данные
Мы знаем, что json — это подмножество нотации объектов js, и его стандартное определение имеет следующие правила:
- Данные находятся в парах имени, значения
- Данные разделены запятыми
- Фигурные скобки содержат объект
- Квадратные скобки содержат массивы
Итак, некоторые распространенные типы данных, такие как строки, логические значения, null, undefined, числа и функции ссылочного типа, объекты и массивы, принадлежат json? Или его стробирование поддерживает преобразование?
Я провел некоторые проверки случаев, и результаты будут опубликованы прямо здесь, Если вам интересно, вы можете проверить, так ли это.
JSON.parse('true') //true
JSON.parse('false') //false
JSON.parse('str') //Uncaught SyntaxError: Unexpected token d in JSON at position 0
JSON.parse('345str') //Uncaught SyntaxError: Unexpected token d in JSON at position 3 ,其报错的位置是出现字符串非数字的时候
JSON.parse('345') //345
JSON.parse('null') //null
JSON.parse("undefined") //Uncaught SyntaxError: Unexpected token d in JSON at position 0
JSON.parse("[]") //[]
JSON.parse('[1,"5"]')//[1,"5"]
JSON.parse("{}")//{}
JSON.parse('{1,5}')//Uncaught SyntaxError: Unexpected token d in JSON at position 1
JSON.parse('{1:1}')//Uncaught SyntaxError: Unexpected token d in JSON at position 1
JSON.parse('{"name":1}')//{name:1}
Отслеживание источника
Чтобы понять, почему такой результат, нам нужно проанализировать, какая логика написана внизу его метода разбора.
Основное внимание здесь уделяется анализу того, почему эти типы пар не ключ-значение поддерживаются, а некоторые нет.
Прежде всего, нам необходимо иметь общее представление о концепции: перед синтаксическим анализом String выполняется форматирование строки, чтобы убедиться, что все символы допустимы, а затем они классифицируются в соответствии с первым символом, а те, которые не соответствует ожиданиям, будет сообщено как не ожидаемая ошибка символа. Тогда он будет характерно различать, включая, помимо прочего, следующие особые случаи.
персонаж | Функции вызова |
---|---|
{ | ParseJsonObject |
f | определить, является ли оно ложным |
t | определить, верно ли |
n | Определить, является ли он нулевым |
Содержит числа, отмеченные от 0 до 9 или отрицательные числа - начиная с | Проверить, является ли целое числом |
Отслеживание источника: общая логика
Мы нашли следующий код с точки зрения исходного кода: чтобы его увидеть, нужно перелезть через стену. Соответствующий адрес исходного файла:
// 情况一 :发现了首字符是字符串标识的引号,用ParseJsonString实现
if (c0_ == '"') return ParseJsonString();
// 情况二 :发现是0-9 数字开始,或者 - 开始的,有可能是数字类型,用转换为数字的方法进行转换
if ((c0_ >= '0' && c0_ <= '9') || c0_ == '-') return ParseJsonNumber();
// 情况三 :发现开始是对象左侧标记 { ,用json对象的解析方法
if (c0_ == '{') return ParseJsonObject();
// 情况四 :发现是 [ 开始的,尝试用数组转换的方法去转换
if (c0_ == '[') return ParseJsonArray();
// 情况五 :排除特殊的一些数据类型,比如true,false,null的字符串化
if (c0_ == 'f') {
if (AdvanceGetChar() == 'a' && AdvanceGetChar() == 'l' &&
AdvanceGetChar() == 's' && AdvanceGetChar() == 'e') {
AdvanceSkipWhitespace();
return factory()->false_value();
}
return ReportUnexpectedCharacter();
}
if (c0_ == 't') {
if (AdvanceGetChar() == 'r' && AdvanceGetChar() == 'u' &&
AdvanceGetChar() == 'e') {
AdvanceSkipWhitespace();
return factory()->true_value();
}
return ReportUnexpectedCharacter();
}
if (c0_ == 'n') {
if (AdvanceGetChar() == 'u' && AdvanceGetChar() == 'l' &&
AdvanceGetChar() == 'l') {
AdvanceSkipWhitespace();
return factory()->null_value();
}
return ReportUnexpectedCharacter();
}
Отслеживание исходного кода: специальные методы
ParseJsonString
Поскольку язык этой части кода сам по себе не изучен, давайте просто проанализируем некоторую логику, обрабатываемую в нем, в основном касающуюся того, является ли содержимое одним байтом, и обработкой некоторых специальных символов.
template <bool seq_one_byte>
bool JsonParser<seq_one_byte>::ParseJsonString(Handle<String> expected) {
int length = expected->length();
if (source_->length() - position_ - 1 > length) {
DisallowHeapAllocation no_gc;
String::FlatContent content = expected->GetFlatContent();
if (content.IsOneByte()) {
DCHECK_EQ('"', c0_);
const uint8_t* input_chars = seq_source_->GetChars() + position_ + 1;
const uint8_t* expected_chars = content.ToOneByteVector().start();
for (int i = 0; i < length; i++) {
uint8_t c0 = input_chars[i];
if (c0 != expected_chars[i] || c0 == '"' || c0 < 0x20 || c0 == '\\') {
return false;
}
}
if (input_chars[length] == '"') {
position_ = position_ + length + 1;
AdvanceSkipWhitespace();
return true;
}
}
}
return false;
}
ParseJsonString
ParseJsonArray
Суть в том, чтобы иметь дело с различием между тем, является ли его конец ], предпосылкой обработки массива является то, что терминатор справа должен быть ] . Если это не так, то он будет преобразован в соответствии с ParseJsonValue.Когда обнаруживается, что преобразование в объект не удается, например, он оказывается нулевым, или в некоторых особых случаях будет сообщено о неожиданной строковой ошибке; Если правая сторона ], это может быть массив, который обрабатывается как простой массив и сложный массив.Простой массив задает фиксированный массив и возвращает этот массив.
// Parse a JSON array. Position must be right at '['.
template <bool seq_one_byte>
Handle<Object> JsonParser<seq_one_byte>::ParseJsonArray() {
HandleScope scope(isolate());
ZoneList<Handle<Object> > elements(4, zone());
DCHECK_EQ(c0_, '[');
ElementKindLattice lattice;
AdvanceSkipWhitespace();
if (c0_ != ']') {
do {
Handle<Object> element = ParseJsonValue();
if (element.is_null()) return ReportUnexpectedCharacter();
elements.Add(element, zone());
lattice.Update(element);
} while (MatchSkipWhiteSpace(','));
if (c0_ != ']') {
return ReportUnexpectedCharacter();
}
}
AdvanceSkipWhitespace();
// Allocate a fixed array with all the elements.
Handle<Object> json_array;
const ElementsKind kind = lattice.GetElementsKind();
switch (kind) {
case PACKED_ELEMENTS:
case PACKED_SMI_ELEMENTS: {
Handle<FixedArray> elems =
factory()->NewFixedArray(elements.length(), pretenure_);
for (int i = 0; i < elements.length(); i++) elems->set(i, *elements[i]);
json_array = factory()->NewJSArrayWithElements(elems, kind, pretenure_);
break;
}
case PACKED_DOUBLE_ELEMENTS: {
Handle<FixedDoubleArray> elems = Handle<FixedDoubleArray>::cast(
factory()->NewFixedDoubleArray(elements.length(), pretenure_));
for (int i = 0; i < elements.length(); i++) {
elems->set(i, elements[i]->Number());
}
json_array = factory()->NewJSArrayWithElements(elems, kind, pretenure_);
break;
}
default:
UNREACHABLE();
}
return scope.CloseAndEscape(json_array);
}
ParseJsonArray
ParseJsonNumber
Ядро оценивает обработку некоторых отрицательных чисел, 0, 1-9, десятичных знаков и других различных ситуаций и выдает символы-исключения, которые не соответствуют ситуации.
template <bool seq_one_byte>
Handle<Object> JsonParser<seq_one_byte>::ParseJsonNumber() {
bool negative = false;
int beg_pos = position_;
if (c0_ == '-') {
Advance();
negative = true;
}
if (c0_ == '0') {
Advance();
// Prefix zero is only allowed if it's the only digit before
// a decimal point or exponent.
if (IsDecimalDigit(c0_)) return ReportUnexpectedCharacter();
} else {
int i = 0;
int digits = 0;
if (c0_ < '1' || c0_ > '9') return ReportUnexpectedCharacter();
do {
i = i * 10 + c0_ - '0';
digits++;
Advance();
} while (IsDecimalDigit(c0_));
if (c0_ != '.' && c0_ != 'e' && c0_ != 'E' && digits < 10) {
SkipWhitespace();
return Handle<Smi>(Smi::FromInt((negative ? -i : i)), isolate());
}
}
if (c0_ == '.') {
Advance();
if (!IsDecimalDigit(c0_)) return ReportUnexpectedCharacter();
do {
Advance();
} while (IsDecimalDigit(c0_));
}
if (AsciiAlphaToLower(c0_) == 'e') {
Advance();
if (c0_ == '-' || c0_ == '+') Advance();
if (!IsDecimalDigit(c0_)) return ReportUnexpectedCharacter();
do {
Advance();
} while (IsDecimalDigit(c0_));
}
int length = position_ - beg_pos;
double number;
if (seq_one_byte) {
Vector<const uint8_t> chars(seq_source_->GetChars() + beg_pos, length);
number = StringToDouble(isolate()->unicode_cache(), chars,
NO_FLAGS, // Hex, octal or trailing junk.
std::numeric_limits<double>::quiet_NaN());
} else {
Vector<uint8_t> buffer = Vector<uint8_t>::New(length);
String::WriteToFlat(*source_, buffer.start(), beg_pos, position_);
Vector<const uint8_t> result =
Vector<const uint8_t>(buffer.start(), length);
number = StringToDouble(isolate()->unicode_cache(), result,
NO_FLAGS, // Hex, octal or trailing junk.
0.0);
buffer.Dispose();
}
SkipWhitespace();
return factory()->NewNumber(number, pretenure_);
}
ParseJsonNumber
ParseJsonObject
Ядро определяет, является ли конец } для обеспечения объекта json, и строго проверяет, следует ли пересматривать базовый формат пары ключ-значение.
// Parse a JSON object. Position must be right at '{'.
template <bool seq_one_byte>
Handle<Object> JsonParser<seq_one_byte>::ParseJsonObject() {
HandleScope scope(isolate());
Handle<JSObject> json_object =
factory()->NewJSObject(object_constructor(), pretenure_);
Handle<Map> map(json_object->map());
int descriptor = 0;
ZoneList<Handle<Object> > properties(8, zone());
DCHECK_EQ(c0_, '{');
bool transitioning = true;
AdvanceSkipWhitespace();
if (c0_ != '}') {
do {
if (c0_ != '"') return ReportUnexpectedCharacter();
int start_position = position_;
Advance();
if (IsDecimalDigit(c0_)) {
ParseElementResult element_result = ParseElement(json_object);
if (element_result == kNullHandle) return Handle<Object>::null();
if (element_result == kElementFound) continue;
}
// Not an index, fallback to the slow path.
position_ = start_position;
#ifdef DEBUG
c0_ = '"';
#endif
Handle<String> key;
Handle<Object> value;
// Try to follow existing transitions as long as possible. Once we stop
// transitioning, no transition can be found anymore.
DCHECK(transitioning);
// First check whether there is a single expected transition. If so, try
// to parse it first.
bool follow_expected = false;
Handle<Map> target;
if (seq_one_byte) {
key = TransitionArray::ExpectedTransitionKey(map);
follow_expected = !key.is_null() && ParseJsonString(key);
}
// If the expected transition hits, follow it.
if (follow_expected) {
target = TransitionArray::ExpectedTransitionTarget(map);
} else {
// If the expected transition failed, parse an internalized string and
// try to find a matching transition.
key = ParseJsonInternalizedString();
if (key.is_null()) return ReportUnexpectedCharacter();
target = TransitionArray::FindTransitionToField(map, key);
// If a transition was found, follow it and continue.
transitioning = !target.is_null();
}
if (c0_ != ':') return ReportUnexpectedCharacter();
AdvanceSkipWhitespace();
value = ParseJsonValue();
if (value.is_null()) return ReportUnexpectedCharacter();
if (transitioning) {
PropertyDetails details =
target->instance_descriptors()->GetDetails(descriptor);
Representation expected_representation = details.representation();
if (value->FitsRepresentation(expected_representation)) {
if (expected_representation.IsHeapObject() &&
!target->instance_descriptors()
->GetFieldType(descriptor)
->NowContains(value)) {
Handle<FieldType> value_type(
value->OptimalType(isolate(), expected_representation));
Map::GeneralizeField(target, descriptor, details.constness(),
expected_representation, value_type);
}
DCHECK(target->instance_descriptors()
->GetFieldType(descriptor)
->NowContains(value));
properties.Add(value, zone());
map = target;
descriptor++;
continue;
} else {
transitioning = false;
}
}
DCHECK(!transitioning);
// Commit the intermediate state to the object and stop transitioning.
CommitStateToJsonObject(json_object, map, &properties);
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key, value)
.Check();
} while (transitioning && MatchSkipWhiteSpace(','));
// If we transitioned until the very end, transition the map now.
if (transitioning) {
CommitStateToJsonObject(json_object, map, &properties);
} else {
while (MatchSkipWhiteSpace(',')) {
HandleScope local_scope(isolate());
if (c0_ != '"') return ReportUnexpectedCharacter();
int start_position = position_;
Advance();
if (IsDecimalDigit(c0_)) {
ParseElementResult element_result = ParseElement(json_object);
if (element_result == kNullHandle) return Handle<Object>::null();
if (element_result == kElementFound) continue;
}
// Not an index, fallback to the slow path.
position_ = start_position;
#ifdef DEBUG
c0_ = '"';
#endif
Handle<String> key;
Handle<Object> value;
key = ParseJsonInternalizedString();
if (key.is_null() || c0_ != ':') return ReportUnexpectedCharacter();
AdvanceSkipWhitespace();
value = ParseJsonValue();
if (value.is_null()) return ReportUnexpectedCharacter();
JSObject::DefinePropertyOrElementIgnoreAttributes(json_object, key,
value)
.Check();
}
}
if (c0_ != '}') {
return ReportUnexpectedCharacter();
}
}
AdvanceSkipWhitespace();
return scope.CloseAndEscape(json_object);
}
ParseJsonObject
мой метод переопределить
Предполагая, что если браузер не поддерживает эти методы внизу, как мы можем инкапсулировать функцию с помощью js внизу? Вы можете обратиться к одному из моих случаев. (только для справки)
Метод такого анализа реализован с JS: CodePen Case, чтобы быть улучшенным