Goran Lazarevski 669cf5383b Lucky World source code
2025-03-18 19:25:25 +01:00

1151 lines
26 KiB
C++

// Copyright 2015-2019 Mail.Ru Group. All Rights Reserved.
#include "VaRestJsonParser.h"
#include "VaRestJsonObject.h"
#include "Dom/JsonObject.h"
#include "Dom/JsonValue.h"
uint32 FUtf8Helper::CodepointFromUtf8(const ANSICHAR*& SourceString, const uint32 SourceLengthRemaining)
{
checkSlow(SourceLengthRemaining > 0);
const ANSICHAR* OctetPtr = SourceString;
uint32 Codepoint = 0;
uint32 Octet = (uint32)((uint8)*SourceString);
uint32 Octet2, Octet3, Octet4;
if (Octet < 128) // one octet char: 0 to 127
{
++SourceString; // skip to next possible start of codepoint.
return Octet;
}
else if (Octet < 192) // bad (starts with 10xxxxxx).
{
// Apparently each of these is supposed to be flagged as a bogus
// char, instead of just resyncing to the next valid codepoint.
++SourceString; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
else if (Octet < 224) // two octets
{
// Ensure our string has enough characters to read from
if (SourceLengthRemaining < 2)
{
// Skip to end and write out a single char (we always have room for at least 1 char)
SourceString += SourceLengthRemaining;
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet -= (128 + 64);
Octet2 = (uint32)((uint8) * (++OctetPtr));
if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Codepoint = ((Octet << 6) | (Octet2 - 128));
if ((Codepoint >= 0x80) && (Codepoint <= 0x7FF))
{
SourceString += 2; // skip to next possible start of codepoint.
return Codepoint;
}
}
else if (Octet < 240) // three octets
{
// Ensure our string has enough characters to read from
if (SourceLengthRemaining < 3)
{
// Skip to end and write out a single char (we always have room for at least 1 char)
SourceString += SourceLengthRemaining;
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet -= (128 + 64 + 32);
Octet2 = (uint32)((uint8) * (++OctetPtr));
if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet3 = (uint32)((uint8) * (++OctetPtr));
if ((Octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Codepoint = (((Octet << 12)) | ((Octet2 - 128) << 6) | ((Octet3 - 128)));
// UTF-8 characters cannot be in the UTF-16 surrogates range
if (StringConv::IsHighSurrogate(Codepoint) || StringConv::IsLowSurrogate(Codepoint))
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
// 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge.
if ((Codepoint >= 0x800) && (Codepoint <= 0xFFFD))
{
SourceString += 3; // skip to next possible start of codepoint.
return Codepoint;
}
}
else if (Octet < 248) // four octets
{
// Ensure our string has enough characters to read from
if (SourceLengthRemaining < 4)
{
// Skip to end and write out a single char (we always have room for at least 1 char)
SourceString += SourceLengthRemaining;
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet -= (128 + 64 + 32 + 16);
Octet2 = (uint32)((uint8) * (++OctetPtr));
if ((Octet2 & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet3 = (uint32)((uint8) * (++OctetPtr));
if ((Octet3 & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet4 = (uint32)((uint8) * (++OctetPtr));
if ((Octet4 & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Codepoint = (((Octet << 18)) | ((Octet2 - 128) << 12) |
((Octet3 - 128) << 6) | ((Octet4 - 128)));
if ((Codepoint >= 0x10000) && (Codepoint <= 0x10FFFF))
{
SourceString += 4; // skip to next possible start of codepoint.
return Codepoint;
}
}
// Five and six octet sequences became illegal in rfc3629.
// We throw the codepoint away, but parse them to make sure we move
// ahead the right number of bytes and don't overflow the buffer.
else if (Octet < 252) // five octets
{
// Ensure our string has enough characters to read from
if (SourceLengthRemaining < 5)
{
// Skip to end and write out a single char (we always have room for at least 1 char)
SourceString += SourceLengthRemaining;
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
SourceString += 5; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
else // six octets
{
// Ensure our string has enough characters to read from
if (SourceLengthRemaining < 6)
{
// Skip to end and write out a single char (we always have room for at least 1 char)
SourceString += SourceLengthRemaining;
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
Octet = (uint32)((uint8) * (++OctetPtr));
if ((Octet & (128 + 64)) != 128) // Format isn't 10xxxxxx?
{
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
SourceString += 6; // skip to next possible start of codepoint.
return UNICODE_BOGUS_CHAR_CODEPOINT;
}
++SourceString; // Sequence was not valid UTF-8. Skip the first byte and continue.
return UNICODE_BOGUS_CHAR_CODEPOINT; // catch everything else.
}
FJSONState::FJSONState()
: Notation(EJSONNotation::NONE)
, bEscape(false)
, bError(false)
, Quote(UNICODE_BOGUS_CHAR_CODEPOINT)
{
Key.Reserve(1024);
Data.Reserve(4096);
Root = TSharedPtr<FJsonObject>(nullptr);
Objects.Reserve(64);
Tokens.Reserve(64);
Tokens.Add(EJSONToken::ROOT);
Size = 0;
}
EJSONToken FJSONState::GetToken(int32 Index)
{
return Tokens.Num() > Index ? Tokens.Last(Index) : EJSONToken::ERROR;
}
bool FJSONState::CheckTokens(EJSONToken T1)
{
return T1 == GetToken(0);
}
bool FJSONState::CheckTokens(EJSONToken T1, EJSONToken T2)
{
return T1 == GetToken(1) && T2 == GetToken(0);
}
bool FJSONState::CheckTokens(EJSONToken T1, EJSONToken T2, EJSONToken T3)
{
return T1 == GetToken(2) && T2 == GetToken(1) && T3 == GetToken(0);
}
void FJSONState::PopToken(int32 Num)
{
if (Num > 0)
{
if (Tokens.Num() >= Num)
{
Tokens.RemoveAt(Tokens.Num() - Num, Num, false);
}
else
{
bError = true;
}
}
Notation = EJSONNotation::NONE;
}
void FJSONState::PopObject()
{
if (Objects.Num() > 0)
{
const auto Object = Objects.Pop(false);
if (Object->Type == EJson::Object)
{
return;
}
}
bError = true;
}
void FJSONState::PopArray()
{
if (Objects.Num() > 0)
{
const auto Object = Objects.Pop(false);
if (Object->Type == EJson::Array)
{
return;
}
}
bError = true;
}
void FJSONState::PopValue(bool bCheckType)
{
if (Objects.Num() > 0)
{
const auto Value = Objects.Last(0);
if (Value->Type == EJson::Object || Value->Type == EJson::Array)
{
if (bCheckType)
{
bError = true;
}
}
else
{
Objects.Pop(false);
if (Objects.Num() > 0)
{
switch (Value->Type)
{
case EJson::Null:
{
const auto LowerCase = Data.ToLower();
if (LowerCase != TEXT("null"))
{
bError = true;
}
break;
}
case EJson::String:
{
FJsonValueNonConstString* JsonValueString = ((FJsonValueNonConstString*)Value.Get());
JsonValueString->AsNonConstString() = Data;
JsonValueString->AsNonConstString().Shrink();
Size += JsonValueString->AsNonConstString().GetAllocatedSize();
break;
}
case EJson::Number:
{
const FString LowerCase = Data.ToLower();
int32 ePosition = INDEX_NONE;
LowerCase.FindChar('e', ePosition);
if (ePosition == INDEX_NONE)
{
if (LowerCase.IsNumeric())
{
((FJsonValueNonConstNumber*)Value.Get())->AsNonConstNumber() = FCString::Atod(*LowerCase);
}
else
{
bError = true;
}
}
else if (LowerCase.Len() > ePosition + 2)
{
const FString Left = LowerCase.Left(ePosition);
const FString Rigth = LowerCase.Right(LowerCase.Len() - ePosition - 1);
if (Left.IsNumeric() && Rigth.IsNumeric())
{
((FJsonValueNonConstNumber*)Value.Get())->AsNonConstNumber() = FCString::Atod(*Left) * FMath::Pow(10.f, FCString::Atoi(*Rigth));
}
else
{
bError = true;
}
}
else
{
bError = true;
}
break;
}
case EJson::Boolean:
{
const auto LowerCase = Data.ToLower();
if (LowerCase == TEXT("true"))
{
((FJsonValueNonConstBoolean*)Value.Get())->AsNonConstBool() = true;
}
else if (LowerCase == TEXT("false"))
{
((FJsonValueNonConstBoolean*)Value.Get())->AsNonConstBool() = false;
}
else
{
bError = true;
}
break;
}
default:
{
bError = true;
return;
}
}
ClearData();
const auto Container = Objects.Last(0);
if (Container->Type == EJson::Object)
{
if (Key.Len() > 0)
{
FString KeyCopy = Key;
KeyCopy.Shrink();
Container->AsObject()->SetField(KeyCopy, Value);
Size += KeyCopy.GetAllocatedSize();
ClearKey();
}
else
{
bError = true;
}
}
else if (Container->Type == EJson::Array)
{
((FJsonValueNonConstArray*)Container.Get())->AsNonConstArray().Add(Value);
}
else
{
bError = true;
}
}
else
{
bError = true;
}
}
}
else
{
bError = true;
}
}
FJsonValue* FJSONState::GetLast()
{
if (Objects.Num() > 0)
{
return Objects.Last(0).Get();
}
bError = true;
return nullptr;
}
FJsonValueObject* FJSONState::GetObject()
{
FJsonValue* Value = GetLast();
if (Value != nullptr && Value->Type == EJson::Object)
{
return (FJsonValueObject*)Value;
}
bError = true;
return nullptr;
}
FJsonValueNonConstArray* FJSONState::GetArray()
{
FJsonValue* Value = GetLast();
if (Value != nullptr && Value->Type == EJson::Array)
{
return (FJsonValueNonConstArray*)Value;
}
bError = true;
return nullptr;
}
TSharedPtr<FJsonValueObject> FJSONState::PushObject()
{
TSharedPtr<FJsonValueObject> Result(new FJsonValueObject(MakeShared<FJsonObject>()));
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueObject>) + sizeof(FJsonValueObject);
return Result;
}
TSharedPtr<FJsonValueObject> FJSONState::PushObject(TSharedPtr<FJsonObject> Object)
{
TSharedPtr<FJsonValueObject> Result(new FJsonValueObject(Object));
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueObject>) + sizeof(FJsonValueObject);
return Result;
}
TSharedPtr<FJsonValueNonConstArray> FJSONState::PushArray()
{
const TArray<TSharedPtr<FJsonValue>> Empty;
TSharedPtr<FJsonValueNonConstArray> Result(new FJsonValueNonConstArray(Empty));
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueNonConstArray>) + sizeof(FJsonValueNonConstArray);
return Result;
}
TSharedPtr<FJsonValueNonConstBoolean> FJSONState::PushBoolean()
{
TSharedPtr<FJsonValueNonConstBoolean> Result(new FJsonValueNonConstBoolean(false));
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueNonConstBoolean>) + sizeof(FJsonValueNonConstBoolean);
return Result;
}
TSharedPtr<FJsonValueNull> FJSONState::PushNull()
{
TSharedPtr<FJsonValueNull> Result(new FJsonValueNull());
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueNull>) + sizeof(FJsonValueNull);
return Result;
}
TSharedPtr<FJsonValueNonConstNumber> FJSONState::PushNumber()
{
TSharedPtr<FJsonValueNonConstNumber> Result(new FJsonValueNonConstNumber(0.f));
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueNonConstNumber>) + sizeof(FJsonValueNonConstNumber);
return Result;
}
TSharedPtr<FJsonValueNonConstString> FJSONState::PushString()
{
TSharedPtr<FJsonValueNonConstString> Result(new FJsonValueNonConstString(TEXT("")));
Objects.Add(Result);
Size += sizeof(TSharedPtr<FJsonValueNonConstString>) + sizeof(FJsonValueNonConstString);
return Result;
}
void FJSONState::ClearData()
{
Data.Reset();
}
void FJSONState::ClearKey()
{
Key.Reset();
}
void FJSONState::DataToKey()
{
ClearKey();
Key += Data;
ClearData();
}
void FJSONState::Error()
{
bError = true;
}
FJSONReader::FJSONReader()
{
}
bool FJSONReader::IsNewLine(const TCHAR& Char)
{
return Char == '\n';
}
bool FJSONReader::IsSpace(const TCHAR& Char)
{
return IsNewLine(Char) || Char == ' ' || Char == '\t' || Char == '\r';
}
bool FJSONReader::FindToken(const TCHAR& Char)
{
if (State.bEscape)
{
return false;
}
if (State.Notation != EJSONNotation::STRING)
{
switch (Char)
{
case '{': State.Tokens.Add(EJSONToken::CURLY_BEGIN); return true;
case '}': State.Tokens.Add(EJSONToken::CURLY_END); return true;
case '[': State.Tokens.Add(EJSONToken::SQUARE_BEGIN); return true;
case ']': State.Tokens.Add(EJSONToken::SQUARE_END); return true;
case ',': State.Tokens.Add(EJSONToken::COMMA); return true;
case ':': State.Tokens.Add(EJSONToken::COLON); return true;
}
}
return false;
}
void FJSONReader::UpdateNotation()
{
switch (State.GetToken())
{
case EJSONToken::ROOT:
{
return;
}
case EJSONToken::CURLY_BEGIN:
{
if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::CURLY_BEGIN)) // Object in array "[{"
{
State.Notation = EJSONNotation::OBJECT;
auto Value = State.GetArray();
if (Value != nullptr)
{
Value->AsNonConstArray().Add(State.PushObject());
}
else
{
State.Error();
}
}
else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::CURLY_BEGIN)) // Object in key "{:{"
{
if (State.Key.Len() > 0)
{
State.Notation = EJSONNotation::OBJECT;
const auto Value = State.GetObject();
if (Value != nullptr)
{
Value->AsObject()->SetField(State.Key, State.PushObject());
State.ClearKey();
}
else
{
State.Error();
}
}
else
{
State.Error();
}
}
else if (State.CheckTokens(EJSONToken::ROOT, EJSONToken::CURLY_BEGIN)) // Root object "{"
{
if (State.Root.IsValid())
{
State.Error();
}
else
{
State.Root = MakeShared<FJsonObject>();
State.PushObject(State.Root); // add root object
State.Notation = EJSONNotation::OBJECT;
}
}
else
{
State.Error();
}
break;
}
case EJSONToken::CURLY_END:
{
if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::CURLY_END)) // Close object "{}"
{
State.PopToken(2); // pop "{}"
State.PopObject(); // remove object
}
else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::CURLY_END)) // Close object "{:}"
{
State.PopToken(3); // pop "{:}"
State.PopValue(); // remove value
State.PopObject(); // remove object
}
else
{
State.Error();
}
if (State.CheckTokens(EJSONToken::COLON)) // Object in object ":"
{
State.PopToken(1); // pop ":"
}
State.Notation = EJSONNotation::SKIP;
break;
}
case EJSONToken::SQUARE_BEGIN:
{
if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::SQUARE_BEGIN)) // Array in array "[["
{
State.Notation = EJSONNotation::ARRAY;
auto Value = State.GetArray();
if (Value != nullptr)
{
Value->AsNonConstArray().Add(State.PushArray());
}
else
{
State.Error();
}
}
else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::SQUARE_BEGIN)) // Array in key "{:["
{
State.Notation = EJSONNotation::ARRAY;
if (State.Key.Len() > 0)
{
const auto Value = State.GetObject();
if (Value != nullptr)
{
Value->AsObject()->SetField(State.Key, State.PushArray());
State.ClearKey();
}
else
{
State.Error();
}
}
else
{
State.Error();
}
}
else if (State.CheckTokens(EJSONToken::ROOT, EJSONToken::SQUARE_BEGIN)) // Root array "{"
{
State.Error(); // Not support
}
else
{
State.Error();
}
break;
}
case EJSONToken::SQUARE_END:
{
if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::SQUARE_END)) // Close array "[]"
{
State.PopToken(2); // remove token "[]"
State.PopValue(false); // remove value if exists
State.PopArray(); // remove array
if (State.CheckTokens(EJSONToken::COLON)) // Array in object ":"
{
State.PopToken(1); // pop ":"
}
}
else
{
State.Error();
}
State.Notation = EJSONNotation::SKIP;
break;
}
case EJSONToken::COMMA:
{
if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON, EJSONToken::COMMA)) // Next record in object "{:,"
{
State.PopToken(2); // remove token ":,"
State.PopValue(false); // remove value
State.Notation = EJSONNotation::OBJECT;
}
else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COMMA)) // Next record in object "{,"
{
State.PopToken(1); // remove token ","
State.Notation = EJSONNotation::OBJECT;
}
else if (State.CheckTokens(EJSONToken::SQUARE_BEGIN, EJSONToken::COMMA)) // Next record in array "[,"
{
State.PopToken(1); // remove token ","
State.PopValue(false); // remove value
State.Notation = EJSONNotation::ARRAY;
}
else
{
State.Error();
}
break;
}
case EJSONToken::COLON:
{
if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON)) // Object key close "{:"
{
State.Notation = EJSONNotation::OBJECT;
if (State.Data.Len() > 0)
{
State.DataToKey();
}
else
{
State.Error();
}
}
else
{
State.Error();
}
break;
}
case EJSONToken::ERROR:
{
State.Error();
break;
}
}
if (!State.bError && State.Notation == EJSONNotation::NONE)
{
UpdateNotation();
}
}
void FJSONReader::ReadAsString(const TCHAR& Char)
{
if (IsNewLine(Char))
{
State.Error();
return;
}
if (!State.bEscape && State.Quote == Char)
{
State.Quote = UNICODE_BOGUS_CHAR_CODEPOINT;
State.Notation = EJSONNotation::SKIP;
}
else
{
if (State.bEscape)
{
switch (Char)
{
case 'n': State.Data.AppendChar('\n'); break;
case 't': State.Data.AppendChar('\t'); break;
default: State.Data.AppendChar(Char); break;
}
}
else
{
State.Data.AppendChar(Char);
}
}
}
void FJSONReader::ReadAsStringSpecial(const TCHAR& Char)
{
if (IsSpace(Char) && State.Data.Len() > 0)
{
State.Notation = EJSONNotation::SKIP;
return;
}
State.Data.AppendChar(Char);
}
void FJSONReader::ReadAsNumber(const TCHAR& Char)
{
if (IsSpace(Char) && State.Data.Len() > 0)
{
State.Notation = EJSONNotation::SKIP;
return;
}
if ((Char >= '0' && Char <= '9') || Char == '-' || Char == '.' || Char == '+' || Char == 'e' || Char == 'E')
{
State.Data.AppendChar(Char);
}
else
{
State.Error();
}
}
void FJSONReader::ReadBasicValue(const TCHAR& Char)
{
switch (Char)
{
case 'T':
case 't':
case 'F':
case 'f':
{
State.PushBoolean();
State.Notation = EJSONNotation::STRING_SPECIAL;
ReadAsStringSpecial(Char);
return;
}
case 'N':
case 'n':
{
State.PushNull();
State.Notation = EJSONNotation::STRING_SPECIAL;
ReadAsStringSpecial(Char);
return;
}
case '\'':
case '"':
{
State.PushString();
State.Notation = EJSONNotation::STRING;
State.Quote = Char;
return;
}
}
if ((Char >= '0' && Char <= '9') || Char == '-')
{
State.PushNumber();
State.Notation = EJSONNotation::NUMBER;
ReadAsNumber(Char);
return;
}
}
void FJSONReader::ReadAsArray(const TCHAR& Char)
{
if (IsSpace(Char))
{
return;
}
ReadBasicValue(Char);
}
void FJSONReader::ReadAsObject(const TCHAR& Char)
{
if (IsSpace(Char))
{
return;
}
if (State.CheckTokens(EJSONToken::CURLY_BEGIN)) // read key "{"
{
if (Char == '\'' || Char == '"')
{
State.Notation = EJSONNotation::STRING;
State.Quote = Char;
}
else
{
State.Notation = EJSONNotation::STRING_SPECIAL;
ReadAsStringSpecial(Char);
}
}
else if (State.CheckTokens(EJSONToken::CURLY_BEGIN, EJSONToken::COLON)) // read value "{:"
{
ReadBasicValue(Char);
}
}
void FJSONReader::Skip(const TCHAR& Char)
{
if (!IsSpace(Char))
{
State.Error();
}
}
bool FJSONReader::Read(const TCHAR Char)
{
if (Char == '\\' && !State.bEscape)
{
State.bEscape = true;
return true;
}
if (FindToken(Char))
{
State.Notation = EJSONNotation::NONE;
UpdateNotation();
return true;
}
switch (State.Notation)
{
case EJSONNotation::NONE: UpdateNotation(); break;
case EJSONNotation::STRING: ReadAsString(Char); break;
case EJSONNotation::STRING_SPECIAL: ReadAsStringSpecial(Char); break;
case EJSONNotation::NUMBER: ReadAsNumber(Char); break;
case EJSONNotation::ARRAY: ReadAsArray(Char); break;
case EJSONNotation::OBJECT: ReadAsObject(Char); break;
case EJSONNotation::SKIP: Skip(Char); break;
}
if (State.bError)
{
State.Root = TSharedPtr<FJsonObject>(nullptr);
State.Size = 0;
return false;
}
State.bEscape = false;
return true;
}
FJSONWriter::FJSONWriter()
{
}
bool FJSONWriter::GetStartChar(const TSharedPtr<FJsonValue>& JsonValue, FString& Str)
{
switch (JsonValue->Type)
{
case EJson::Object:
Str = FString(TEXT("{"));
break;
case EJson::Array:
Str = FString(TEXT("["));
break;
case EJson::String:
Str = FString(TEXT("\""));
break;
default:
return false;
break;
}
return true;
}
bool FJSONWriter::GetEndChar(const TSharedPtr<FJsonValue>& JsonValue, FString& Str)
{
switch (JsonValue->Type)
{
case EJson::Object:
Str = FString(TEXT("}"));
break;
case EJson::Array:
Str = FString(TEXT("]"));
break;
case EJson::String:
Str = FString(TEXT("\""));
break;
default:
return false;
break;
}
return true;
}
void FJSONWriter::Write(TSharedPtr<FJsonValue> JsonValue, FArchive* Writer, bool IsLastElement)
{
FString Str;
FArchive& Ar = *Writer;
if (GetStartChar(JsonValue, Str))
{
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
}
switch (JsonValue->Type)
{
case EJson::Object:
{
int ElementsCount = 0;
auto Values = JsonValue->AsObject()->Values;
for (auto& ChildJsonPair : Values)
{
Str = FString(TEXT("\""));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
const TCHAR* BufferPtr = *ChildJsonPair.Key;
for (int i = 0; i < ChildJsonPair.Key.Len(); ++i)
{
Str = FString(1, &ChildJsonPair.Key[i]);
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
}
Str = FString(TEXT("\""));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
Str = FString(TEXT(":"));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
++ElementsCount;
Write(ChildJsonPair.Value, Writer, ElementsCount >= Values.Num());
}
break;
}
case EJson::Array:
{
int ElementsCount = 0;
auto Array = JsonValue->AsArray();
for (auto& ChildJsonValue : Array)
{
++ElementsCount;
Write(ChildJsonValue, Writer, ElementsCount >= Array.Num());
}
break;
}
default:
{
const FString Value = JsonValue->AsString();
const TCHAR* BufferPtr = *Value;
for (int i = 0; i < Value.Len(); ++i)
{
Str = FString(1, &BufferPtr[i]);
if (Str == TEXT("\""))
{
Str = FString(TEXT("\\"));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
Str = FString(1, &BufferPtr[i]);
}
if (Str == TEXT("\n"))
{
Str = FString(TEXT("\\"));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
Str = FString(TEXT("n"));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
Str = FString(1, &BufferPtr[i]);
}
else if (Str == TEXT("\t"))
{
Str = FString(TEXT("\\"));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
Str = FString(TEXT("t"));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
Str = FString(1, &BufferPtr[i]);
}
else
{
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
}
}
break;
}
}
if (GetEndChar(JsonValue, Str))
{
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
}
if (!IsLastElement)
{
Str = FString(TEXT(","));
UVaRestJsonObject::WriteStringToArchive(Ar, *Str, Str.Len());
}
}