Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/rapidjson/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -1972,6 +1972,8 @@ class GenericValue {
return handler.EndArray(data_.a.size);

case kStringType:
if (data_.f.flags & kRawNumberFlag)
return handler.RawNumber(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);
return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0);

default:
Expand Down Expand Up @@ -1999,6 +2001,7 @@ class GenericValue {
kStringFlag = 0x0400,
kCopyFlag = 0x0800,
kInlineStrFlag = 0x1000,
kRawNumberFlag = 0x2000,

// Initial flags of different types.
kNullFlag = kNullType,
Expand Down Expand Up @@ -2831,6 +2834,7 @@ class GenericDocument : public GenericValue<Encoding, Allocator> {
new (stack_.template Push<ValueType>()) ValueType(str, length, GetAllocator());
else
new (stack_.template Push<ValueType>()) ValueType(str, length);
stack_.template Top<ValueType>()->data_.f.flags |= ValueType::kRawNumberFlag;
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion include/rapidjson/prettywriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class PrettyWriter : public Writer<OutputStream, SourceEncoding, TargetEncoding,
RAPIDJSON_ASSERT(str != 0);
(void)copy;
PrettyPrefix(kNumberType);
return Base::EndValue(Base::WriteString(str, length));
return Base::EndValue(Base::WriteRawValue(str, length));
}

bool String(const Ch* str, SizeType length, bool copy = false) {
Expand Down
2 changes: 1 addition & 1 deletion include/rapidjson/writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ class Writer {
RAPIDJSON_ASSERT(str != 0);
(void)copy;
Prefix(kNumberType);
return EndValue(WriteString(str, length));
return EndValue(WriteRawValue(str, length));
}

bool String(const Ch* str, SizeType length, bool copy = false) {
Expand Down
59 changes: 59 additions & 0 deletions test/unittest/documenttest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "unittest.h"
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/prettywriter.h"
#include "rapidjson/filereadstream.h"
#include "rapidjson/encodedstream.h"
#include "rapidjson/stringbuffer.h"
Expand Down Expand Up @@ -669,6 +670,64 @@ TYPED_TEST(DocumentMove, MoveAssignmentStack) {
// d1 = d2;
//}

TEST(Document, RawNumberRoundtrip) {
// Parse with kParseNumbersAsStringsFlag, then serialize back
// Numbers should survive the DOM roundtrip without gaining quotes
const char* json = "{\"age\":27,\"pi\":3.14159,\"name\":\"test\"}";

Document d;
d.Parse<kParseNumbersAsStringsFlag>(json);
EXPECT_FALSE(d.HasParseError());

// Verify values are stored as strings internally
EXPECT_TRUE(d["age"].IsString());
EXPECT_STREQ("27", d["age"].GetString());
EXPECT_TRUE(d["pi"].IsString());
EXPECT_STREQ("3.14159", d["pi"].GetString());
EXPECT_TRUE(d["name"].IsString());
EXPECT_STREQ("test", d["name"].GetString());

// Serialize back via Writer
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);

// Numbers should NOT be quoted, but actual strings should be
EXPECT_STREQ("{\"age\":27,\"pi\":3.14159,\"name\":\"test\"}", buffer.GetString());
}

TEST(Document, RawNumberRoundtrip_Array) {
const char* json = "[1, 2.5, \"hello\", 100]";

Document d;
d.Parse<kParseNumbersAsStringsFlag>(json);
EXPECT_FALSE(d.HasParseError());

StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
d.Accept(writer);

EXPECT_STREQ("[1,2.5,\"hello\",100]", buffer.GetString());
}

TEST(Document, RawNumberRoundtrip_PrettyWriter) {
const char* json = "{\"value\":42}";

Document d;
d.Parse<kParseNumbersAsStringsFlag>(json);
EXPECT_FALSE(d.HasParseError());

StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
d.Accept(writer);

// Should contain unquoted 42
const char* s = buffer.GetString();
EXPECT_TRUE(strstr(s, ": 42") != NULL);
// Should NOT contain "42"
EXPECT_TRUE(strstr(s, ": \"42\"") == NULL);
}

#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
19 changes: 19 additions & 0 deletions test/unittest/prettywritertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,25 @@ TEST(PrettyWriter, Issue_1336) {
EXPECT_TRUE(writer.IsComplete());
}

TEST(PrettyWriter, RawNumber_NoQuotes) {
StringBuffer buffer;
PrettyWriter<StringBuffer> writer(buffer);
writer.StartObject();
writer.Key("pi");
writer.RawNumber("3.14159", 7);
writer.Key("answer");
writer.RawNumber("42", 2);
writer.Key("label");
writer.String("test");
writer.EndObject();
EXPECT_TRUE(writer.IsComplete());
// Verify numbers are not quoted
const char* s = buffer.GetString();
EXPECT_TRUE(strstr(s, ": 3.14159") != NULL);
EXPECT_TRUE(strstr(s, ": 42") != NULL);
EXPECT_TRUE(strstr(s, ": \"test\"") != NULL);
}

#ifdef __clang__
RAPIDJSON_DIAG_POP
#endif
25 changes: 25 additions & 0 deletions test/unittest/writertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,31 @@ TEST(Writer, RawValue) {
EXPECT_STREQ("{\"a\":1,\"raw\":[\"Hello\\nWorld\", 123.456]}", buffer.GetString());
}

TEST(Writer, RawNumber_NoQuotes) {
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
writer.StartArray();
const char number[] = "3.14159";
writer.RawNumber(number, 4);
writer.RawNumber(number, static_cast<SizeType>(strlen(number)));
writer.EndArray();
EXPECT_TRUE(writer.IsComplete());
EXPECT_STREQ("[3.14,3.14159]", buffer.GetString());
}

TEST(Writer, RawNumber_InObject) {
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
writer.StartObject();
writer.Key("value");
writer.RawNumber("42", 2);
writer.Key("name");
writer.String("test");
writer.EndObject();
EXPECT_TRUE(writer.IsComplete());
EXPECT_STREQ("{\"value\":42,\"name\":\"test\"}", buffer.GetString());
}

TEST(Write, RawValue_Issue1152) {
{
GenericStringBuffer<UTF32<> > sb;
Expand Down