Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
R
rapidjson
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
rapidjson
Commits
c5a4926b
Commit
c5a4926b
authored
Jul 15, 2014
by
Milo Yip
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #67 from miloyip/issue66writerassertion
Fix #66 by adding Writer::Reset() and Writer::IsComplete()
parents
2e0b3de8
27101d9c
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
216 additions
and
46 deletions
+216
-46
prettywriter.h
include/rapidjson/prettywriter.h
+17
-14
writer.h
include/rapidjson/writer.h
+66
-29
unittest.h
test/unittest/unittest.h
+18
-0
writertest.cpp
test/unittest/writertest.cpp
+115
-3
No files found.
include/rapidjson/prettywriter.h
View file @
c5a4926b
...
...
@@ -78,13 +78,13 @@ public:
bool
empty
=
Base
::
level_stack_
.
template
Pop
<
typename
Base
::
Level
>
(
1
)
->
valueCount
==
0
;
if
(
!
empty
)
{
Base
::
os_
.
Put
(
'\n'
);
Base
::
os_
->
Put
(
'\n'
);
WriteIndent
();
}
if
(
!
Base
::
WriteEndObject
())
return
false
;
if
(
Base
::
level_stack_
.
Empty
())
// end of json text
Base
::
os_
.
Flush
();
Base
::
os_
->
Flush
();
return
true
;
}
...
...
@@ -101,13 +101,13 @@ public:
bool
empty
=
Base
::
level_stack_
.
template
Pop
<
typename
Base
::
Level
>
(
1
)
->
valueCount
==
0
;
if
(
!
empty
)
{
Base
::
os_
.
Put
(
'\n'
);
Base
::
os_
->
Put
(
'\n'
);
WriteIndent
();
}
if
(
!
Base
::
WriteEndArray
())
return
false
;
if
(
Base
::
level_stack_
.
Empty
())
// end of json text
Base
::
os_
.
Flush
();
Base
::
os_
->
Flush
();
return
true
;
}
...
...
@@ -137,26 +137,26 @@ protected:
if
(
level
->
inArray
)
{
if
(
level
->
valueCount
>
0
)
{
Base
::
os_
.
Put
(
','
);
// add comma if it is not the first element in array
Base
::
os_
.
Put
(
'\n'
);
Base
::
os_
->
Put
(
','
);
// add comma if it is not the first element in array
Base
::
os_
->
Put
(
'\n'
);
}
else
Base
::
os_
.
Put
(
'\n'
);
Base
::
os_
->
Put
(
'\n'
);
WriteIndent
();
}
else
{
// in object
if
(
level
->
valueCount
>
0
)
{
if
(
level
->
valueCount
%
2
==
0
)
{
Base
::
os_
.
Put
(
','
);
Base
::
os_
.
Put
(
'\n'
);
Base
::
os_
->
Put
(
','
);
Base
::
os_
->
Put
(
'\n'
);
}
else
{
Base
::
os_
.
Put
(
':'
);
Base
::
os_
.
Put
(
' '
);
Base
::
os_
->
Put
(
':'
);
Base
::
os_
->
Put
(
' '
);
}
}
else
Base
::
os_
.
Put
(
'\n'
);
Base
::
os_
->
Put
(
'\n'
);
if
(
level
->
valueCount
%
2
==
0
)
WriteIndent
();
...
...
@@ -165,13 +165,16 @@ protected:
RAPIDJSON_ASSERT
(
type
==
kStringType
);
// if it's in object, then even number should be a name
level
->
valueCount
++
;
}
else
else
{
RAPIDJSON_ASSERT
(
type
==
kObjectType
||
type
==
kArrayType
);
RAPIDJSON_ASSERT
(
!
Base
::
hasRoot_
);
// Should only has one and only one root.
Base
::
hasRoot_
=
true
;
}
}
void
WriteIndent
()
{
size_t
count
=
(
Base
::
level_stack_
.
GetSize
()
/
sizeof
(
typename
Base
::
Level
))
*
indentCharCount_
;
PutN
(
Base
::
os_
,
indentChar_
,
count
);
PutN
(
*
Base
::
os_
,
indentChar_
,
count
);
}
Ch
indentChar_
;
...
...
include/rapidjson/writer.h
View file @
c5a4926b
...
...
@@ -41,8 +41,41 @@ public:
\param levelDepth Initial capacity of stack.
*/
Writer
(
OutputStream
&
os
,
Allocator
*
allocator
=
0
,
size_t
levelDepth
=
kDefaultLevelDepth
)
:
os_
(
os
),
level_stack_
(
allocator
,
levelDepth
*
sizeof
(
Level
)),
doublePrecision_
(
kDefaultDoublePrecision
)
{}
os_
(
&
os
),
level_stack_
(
allocator
,
levelDepth
*
sizeof
(
Level
)),
doublePrecision_
(
kDefaultDoublePrecision
),
hasRoot_
(
false
)
{}
//! Reset the writer with a new stream.
/*!
This function reset the writer with a new stream and default settings,
in order to make a Writer object reusable for output multiple JSONs.
\param os New output stream.
\code
Writer<OutputStream> writer(os1);
writer.StartObject();
// ...
writer.EndObject();
writer.Reset(os2);
writer.StartObject();
// ...
writer.EndObject();
\endcode
*/
void
Reset
(
OutputStream
&
os
)
{
os_
=
&
os
;
doublePrecision_
=
kDefaultDoublePrecision
;
hasRoot_
=
false
;
level_stack_
.
Clear
();
}
//! Checks whether the output is a complete JSON.
/*!
A complete JSON has a complete root object or array.
*/
bool
IsComplete
()
const
{
return
hasRoot_
&&
level_stack_
.
Empty
();
}
//! Set the number of significant digits for \c double values
/*! When writing a \c double value to the \c OutputStream, the number
...
...
@@ -103,7 +136,7 @@ public:
level_stack_
.
template
Pop
<
Level
>
(
1
);
bool
ret
=
WriteEndObject
();
if
(
level_stack_
.
Empty
())
// end of json text
os_
.
Flush
();
os_
->
Flush
();
return
ret
;
}
...
...
@@ -120,7 +153,7 @@ public:
level_stack_
.
template
Pop
<
Level
>
(
1
);
bool
ret
=
WriteEndArray
();
if
(
level_stack_
.
Empty
())
// end of json text
os_
.
Flush
();
os_
->
Flush
();
return
ret
;
}
//@}
...
...
@@ -161,22 +194,22 @@ protected:
static
const
size_t
kDefaultLevelDepth
=
32
;
bool
WriteNull
()
{
os_
.
Put
(
'n'
);
os_
.
Put
(
'u'
);
os_
.
Put
(
'l'
);
os_
.
Put
(
'l'
);
return
true
;
os_
->
Put
(
'n'
);
os_
->
Put
(
'u'
);
os_
->
Put
(
'l'
);
os_
->
Put
(
'l'
);
return
true
;
}
bool
WriteBool
(
bool
b
)
{
if
(
b
)
{
os_
.
Put
(
't'
);
os_
.
Put
(
'r'
);
os_
.
Put
(
'u'
);
os_
.
Put
(
'e'
);
os_
->
Put
(
't'
);
os_
->
Put
(
'r'
);
os_
->
Put
(
'u'
);
os_
->
Put
(
'e'
);
}
else
{
os_
.
Put
(
'f'
);
os_
.
Put
(
'a'
);
os_
.
Put
(
'l'
);
os_
.
Put
(
's'
);
os_
.
Put
(
'e'
);
os_
->
Put
(
'f'
);
os_
->
Put
(
'a'
);
os_
->
Put
(
'l'
);
os_
->
Put
(
's'
);
os_
->
Put
(
'e'
);
}
return
true
;
}
bool
WriteInt
(
int
i
)
{
if
(
i
<
0
)
{
os_
.
Put
(
'-'
);
os_
->
Put
(
'-'
);
i
=
-
i
;
}
return
WriteUint
((
unsigned
)
i
);
...
...
@@ -192,14 +225,14 @@ protected:
do
{
--
p
;
os_
.
Put
(
*
p
);
os_
->
Put
(
*
p
);
}
while
(
p
!=
buffer
);
return
true
;
}
bool
WriteInt64
(
int64_t
i64
)
{
if
(
i64
<
0
)
{
os_
.
Put
(
'-'
);
os_
->
Put
(
'-'
);
i64
=
-
i64
;
}
WriteUint64
((
uint64_t
)
i64
);
...
...
@@ -216,7 +249,7 @@ protected:
do
{
--
p
;
os_
.
Put
(
*
p
);
os_
->
Put
(
*
p
);
}
while
(
p
!=
buffer
);
return
true
;
}
...
...
@@ -233,7 +266,7 @@ protected:
int
ret
=
RAPIDJSON_SNPRINTF
(
buffer
,
sizeof
(
buffer
),
"%.*g"
,
doublePrecision_
,
d
);
RAPIDJSON_ASSERT
(
ret
>=
1
);
for
(
int
i
=
0
;
i
<
ret
;
i
++
)
os_
.
Put
(
buffer
[
i
]);
os_
->
Put
(
buffer
[
i
]);
return
true
;
}
#undef RAPIDJSON_SNPRINTF
...
...
@@ -252,32 +285,32 @@ protected:
#undef Z16
};
os_
.
Put
(
'\"'
);
os_
->
Put
(
'\"'
);
GenericStringStream
<
SourceEncoding
>
is
(
str
);
while
(
is
.
Tell
()
<
length
)
{
const
Ch
c
=
is
.
Peek
();
if
((
sizeof
(
Ch
)
==
1
||
(
unsigned
)
c
<
256
)
&&
escape
[(
unsigned
char
)
c
])
{
is
.
Take
();
os_
.
Put
(
'\\'
);
os_
.
Put
(
escape
[(
unsigned
char
)
c
]);
os_
->
Put
(
'\\'
);
os_
->
Put
(
escape
[(
unsigned
char
)
c
]);
if
(
escape
[(
unsigned
char
)
c
]
==
'u'
)
{
os_
.
Put
(
'0'
);
os_
.
Put
(
'0'
);
os_
.
Put
(
hexDigits
[(
unsigned
char
)
c
>>
4
]);
os_
.
Put
(
hexDigits
[(
unsigned
char
)
c
&
0xF
]);
os_
->
Put
(
'0'
);
os_
->
Put
(
'0'
);
os_
->
Put
(
hexDigits
[(
unsigned
char
)
c
>>
4
]);
os_
->
Put
(
hexDigits
[(
unsigned
char
)
c
&
0xF
]);
}
}
else
Transcoder
<
SourceEncoding
,
TargetEncoding
>::
Transcode
(
is
,
os_
);
Transcoder
<
SourceEncoding
,
TargetEncoding
>::
Transcode
(
is
,
*
os_
);
}
os_
.
Put
(
'\"'
);
os_
->
Put
(
'\"'
);
return
true
;
}
bool
WriteStartObject
()
{
os_
.
Put
(
'{'
);
return
true
;
}
bool
WriteEndObject
()
{
os_
.
Put
(
'}'
);
return
true
;
}
bool
WriteStartArray
()
{
os_
.
Put
(
'['
);
return
true
;
}
bool
WriteEndArray
()
{
os_
.
Put
(
']'
);
return
true
;
}
bool
WriteStartObject
()
{
os_
->
Put
(
'{'
);
return
true
;
}
bool
WriteEndObject
()
{
os_
->
Put
(
'}'
);
return
true
;
}
bool
WriteStartArray
()
{
os_
->
Put
(
'['
);
return
true
;
}
bool
WriteEndArray
()
{
os_
->
Put
(
']'
);
return
true
;
}
void
Prefix
(
Type
type
)
{
(
void
)
type
;
...
...
@@ -285,21 +318,25 @@ protected:
Level
*
level
=
level_stack_
.
template
Top
<
Level
>
();
if
(
level
->
valueCount
>
0
)
{
if
(
level
->
inArray
)
os_
.
Put
(
','
);
// add comma if it is not the first element in array
os_
->
Put
(
','
);
// add comma if it is not the first element in array
else
// in object
os_
.
Put
((
level
->
valueCount
%
2
==
0
)
?
','
:
':'
);
os_
->
Put
((
level
->
valueCount
%
2
==
0
)
?
','
:
':'
);
}
if
(
!
level
->
inArray
&&
level
->
valueCount
%
2
==
0
)
RAPIDJSON_ASSERT
(
type
==
kStringType
);
// if it's in object, then even number should be a name
level
->
valueCount
++
;
}
else
else
{
RAPIDJSON_ASSERT
(
type
==
kObjectType
||
type
==
kArrayType
);
RAPIDJSON_ASSERT
(
!
hasRoot_
);
// Should only has one and only one root.
hasRoot_
=
true
;
}
}
OutputStream
&
os_
;
OutputStream
*
os_
;
internal
::
Stack
<
Allocator
>
level_stack_
;
int
doublePrecision_
;
bool
hasRoot_
;
static
const
int
kDefaultDoublePrecision
=
6
;
...
...
test/unittest/unittest.h
View file @
c5a4926b
...
...
@@ -56,4 +56,22 @@ inline void TempFilename(char *filename) {
filename
[
i
]
=
filename
[
i
+
1
];
}
// Use exception for catching assert
#if _MSC_VER
#pragma warning(disable : 4127)
#endif
class
AssertException
:
public
std
::
exception
{
public
:
AssertException
(
const
char
*
w
)
:
what_
(
w
)
{}
AssertException
(
const
AssertException
&
other
)
:
what_
(
other
.
what_
)
{}
AssertException
&
operator
=
(
const
AssertException
&
rhs
)
{
what_
=
rhs
.
what_
;
return
*
this
;
}
virtual
const
char
*
what
()
const
throw
()
{
return
what_
;
}
private
:
const
char
*
what_
;
};
#define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x))
#endif // UNITTEST_H_
test/unittest/writertest.cpp
View file @
c5a4926b
#include "unittest.h"
#include "rapidjson/document.h"
#include "rapidjson/reader.h"
#include "rapidjson/writer.h"
...
...
@@ -14,6 +15,7 @@ TEST(Writer, Compact) {
reader
.
Parse
<
0
>
(
s
,
writer
);
EXPECT_STREQ
(
"{
\"
hello
\"
:
\"
world
\"
,
\"
t
\"
:true,
\"
f
\"
:false,
\"
n
\"
:null,
\"
i
\"
:123,
\"
pi
\"
:3.1416,
\"
a
\"
:[1,2,3]}"
,
buffer
.
GetString
());
EXPECT_EQ
(
77u
,
buffer
.
GetSize
());
EXPECT_TRUE
(
writer
.
IsComplete
());
}
// json -> parse -> writer -> json
...
...
@@ -25,6 +27,7 @@ TEST(Writer, Compact) {
Reader
reader
;
\
reader
.
Parse
<
0
>
(
s
,
writer
);
\
EXPECT_STREQ
(
json
,
buffer
.
GetString
());
\
EXPECT_TRUE
(
writer
.
IsComplete
());
\
}
TEST
(
Writer
,
Int
)
{
...
...
@@ -80,9 +83,10 @@ TEST(Writer,DoublePrecision) {
reader
.
Parse
<
0
>
(
s
,
writer
.
SetDoublePrecision
(
12
));
EXPECT_EQ
(
writer
.
GetDoublePrecision
(),
12
);
EXPECT_STREQ
(
json
,
buffer
.
GetString
());
buffer
.
Clear
();
}
{
// explicit individual double precisions
buffer
.
Clear
();
writer
.
Reset
(
buffer
);
writer
.
SetDoublePrecision
(
2
);
writer
.
StartArray
();
writer
.
Double
(
1.2345
,
5
);
...
...
@@ -93,11 +97,12 @@ TEST(Writer,DoublePrecision) {
EXPECT_EQ
(
writer
.
GetDoublePrecision
(),
2
);
EXPECT_STREQ
(
json
,
buffer
.
GetString
());
buffer
.
Clear
();
}
{
// write with default precision (output with precision loss)
Document
d
;
d
.
Parse
<
0
>
(
json
);
buffer
.
Clear
();
writer
.
Reset
(
buffer
);
d
.
Accept
(
writer
.
SetDoublePrecision
());
// parsed again to avoid platform-dependent floating point outputs
...
...
@@ -108,7 +113,6 @@ TEST(Writer,DoublePrecision) {
EXPECT_DOUBLE_EQ
(
d
[
1u
].
GetDouble
(),
1.23457
);
EXPECT_DOUBLE_EQ
(
d
[
2u
].
GetDouble
(),
0.123457
);
EXPECT_DOUBLE_EQ
(
d
[
3u
].
GetDouble
(),
1234570
);
buffer
.
Clear
();
}
}
...
...
@@ -119,6 +123,7 @@ TEST(Writer, Transcode) {
Writer
<
StringBuffer
,
UTF16
<>
,
UTF8
<>
>
writer
(
buffer
);
GenericReader
<
UTF8
<>
,
UTF16
<>
>
reader
;
reader
.
Parse
<
0
>
(
s
,
writer
);
EXPECT_TRUE
(
writer
.
IsComplete
());
EXPECT_STREQ
(
"{
\"
hello
\"
:
\"
world
\"
,
\"
t
\"
:true,
\"
f
\"
:false,
\"
n
\"
:null,
\"
i
\"
:123,
\"
pi
\"
:3.1416,
\"
a
\"
:[1,2,3],
\"
dollar
\"
:
\"\x24\"
,
\"
cents
\"
:
\"\xC2\xA2\"
,
\"
euro
\"
:
\"\xE2\x82\xAC\"
,
\"
gclef
\"
:
\"\xF0\x9D\x84\x9E\"
}"
,
buffer
.
GetString
());
}
...
...
@@ -160,3 +165,109 @@ TEST(Writer, OStreamWrapper) {
std
::
string
actual
=
ss
.
str
();
EXPECT_STREQ
(
"{
\"
hello
\"
:
\"
world
\"
,
\"
t
\"
:true,
\"
f
\"
:false,
\"
n
\"
:null,
\"
i
\"
:123,
\"
pi
\"
:3.1416,
\"
a
\"
:[1,2,3]}"
,
actual
.
c_str
());
}
TEST
(
Writer
,
AssertRootMustBeArrayOrObject
)
{
#define T(x)\
{
\
StringBuffer
buffer
;
\
Writer
<
StringBuffer
>
writer
(
buffer
);
\
ASSERT_THROW
(
x
,
AssertException
);
\
}
T
(
writer
.
Bool
(
false
));
T
(
writer
.
Bool
(
true
));
T
(
writer
.
Null
());
T
(
writer
.
Int
(
0
));
T
(
writer
.
Uint
(
0
));
T
(
writer
.
Int64
(
0
));
T
(
writer
.
Uint64
(
0
));
T
(
writer
.
Double
(
0
));
T
(
writer
.
String
(
"foo"
));
#undef T
}
TEST
(
Writer
,
AssertIncorrectObjectLevel
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
writer
.
StartObject
();
writer
.
EndObject
();
ASSERT_THROW
(
writer
.
EndObject
(),
AssertException
);
}
TEST
(
Writer
,
AssertIncorrectArrayLevel
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
writer
.
StartArray
();
writer
.
EndArray
();
ASSERT_THROW
(
writer
.
EndArray
(),
AssertException
);
}
TEST
(
Writer
,
AssertIncorrectEndObject
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
writer
.
StartObject
();
ASSERT_THROW
(
writer
.
EndArray
(),
AssertException
);
}
TEST
(
Writer
,
AssertIncorrectEndArray
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
writer
.
StartObject
();
ASSERT_THROW
(
writer
.
EndArray
(),
AssertException
);
}
TEST
(
Writer
,
AssertObjectKeyNotString
)
{
#define T(x)\
{
\
StringBuffer
buffer
;
\
Writer
<
StringBuffer
>
writer
(
buffer
);
\
writer
.
StartObject
();
\
ASSERT_THROW
(
x
,
AssertException
);
\
}
T
(
writer
.
Bool
(
false
));
T
(
writer
.
Bool
(
true
));
T
(
writer
.
Null
());
T
(
writer
.
Int
(
0
));
T
(
writer
.
Uint
(
0
));
T
(
writer
.
Int64
(
0
));
T
(
writer
.
Uint64
(
0
));
T
(
writer
.
Double
(
0
));
T
(
writer
.
StartObject
());
T
(
writer
.
StartArray
());
#undef T
}
TEST
(
Writer
,
AssertMultipleRoot
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
writer
.
StartObject
();
writer
.
EndObject
();
ASSERT_THROW
(
writer
.
StartObject
(),
AssertException
);
}
TEST
(
Writer
,
RootObjectIsComplete
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
StartObject
();
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
String
(
"foo"
);
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
Int
(
1
);
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
EndObject
();
EXPECT_TRUE
(
writer
.
IsComplete
());
}
TEST
(
Writer
,
RootArrayIsComplete
)
{
StringBuffer
buffer
;
Writer
<
StringBuffer
>
writer
(
buffer
);
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
StartArray
();
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
String
(
"foo"
);
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
Int
(
1
);
EXPECT_FALSE
(
writer
.
IsComplete
());
writer
.
EndArray
();
EXPECT_TRUE
(
writer
.
IsComplete
());
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment