Skip to content

Commit 605cd7e

Browse files
committed
Added StyledStreamWriter, which has no reason to derive from Writer, since its write() method does cannot return a string and must take a stream.
1 parent 8985cee commit 605cd7e

2 files changed

Lines changed: 344 additions & 4 deletions

File tree

include/json/writer.h

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ namespace Json {
3030
{
3131
public:
3232
FastWriter();
33+
virtual ~FastWriter(){}
3334

3435
void enableYAMLCompatibility();
3536

@@ -61,7 +62,7 @@ namespace Json {
6162
*
6263
* \sa Reader, Value, Value::setComment()
6364
*/
64-
class JSON_API StyledWriter
65+
class JSON_API StyledWriter: public Writer
6566
{
6667
public:
6768
StyledWriter();
@@ -98,13 +99,71 @@ namespace Json {
9899
bool addChildValues_;
99100
};
100101

102+
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
103+
to a stream rather than to a string.
104+
*
105+
* The rules for line break and indent are as follow:
106+
* - Object value:
107+
* - if empty then print {} without indent and line break
108+
* - if not empty the print '{', line break & indent, print one value per line
109+
* and then unindent and line break and print '}'.
110+
* - Array value:
111+
* - if empty then print [] without indent and line break
112+
* - if the array contains no object value, empty array or some other value types,
113+
* and all the values fit on one lines, then print the array on a single line.
114+
* - otherwise, it the values do not fit on one line, or the array contains
115+
* object or non empty array, then print one value per line.
116+
*
117+
* If the Value have comments then they are outputed according to their #CommentPlacement.
118+
*
119+
* \param indentation Each level will be indented by this amount extra.
120+
* \sa Reader, Value, Value::setComment()
121+
*/
122+
class JSON_API StyledStreamWriter
123+
{
124+
public:
125+
StyledStreamWriter( std::string indentation="\t" );
126+
~StyledStreamWriter(){}
127+
128+
public:
129+
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
130+
* \param out Stream to write to. (Can be ostringstream, e.g.)
131+
* \param root Value to serialize.
132+
* \note There is no point in deriving from Writer, since write() should not return a value.
133+
*/
134+
void write( std::ostream &out, const Value &root );
135+
136+
private:
137+
void writeValue( const Value &value );
138+
void writeArrayValue( const Value &value );
139+
bool isMultineArray( const Value &value );
140+
void pushValue( const std::string &value );
141+
void writeIndent();
142+
void writeWithIndent( const std::string &value );
143+
void indent();
144+
void unindent();
145+
void writeCommentBeforeValue( const Value &root );
146+
void writeCommentAfterValueOnSameLine( const Value &root );
147+
bool hasCommentForValue( const Value &value );
148+
static std::string normalizeEOL( const std::string &text );
149+
150+
typedef std::vector<std::string> ChildValues;
151+
152+
ChildValues childValues_;
153+
std::ostream* document_;
154+
std::string indentString_;
155+
int rightMargin_;
156+
std::string indentation_;
157+
bool addChildValues_;
158+
};
159+
101160
std::string JSON_API valueToString( Value::Int value );
102161
std::string JSON_API valueToString( Value::UInt value );
103162
std::string JSON_API valueToString( double value );
104163
std::string JSON_API valueToString( bool value );
105164
std::string JSON_API valueToQuotedString( const char *value );
106165

107-
/// \brief Output using the StyledWriter.
166+
/// \brief Output using the StyledStreamWriter.
108167
/// \see Json::operator>>()
109168
std::ostream& operator<<( std::ostream&, const Value &root );
110169

src/lib_json/json_writer.cpp

Lines changed: 283 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,291 @@ StyledWriter::normalizeEOL( const std::string &text )
476476
return normalized;
477477
}
478478

479+
480+
// Class StyledStreamWriter
481+
// //////////////////////////////////////////////////////////////////
482+
483+
StyledStreamWriter::StyledStreamWriter( std::string indentation )
484+
: document_(NULL)
485+
, rightMargin_( 74 )
486+
, indentation_( indentation )
487+
{
488+
}
489+
490+
491+
void
492+
StyledStreamWriter::write( std::ostream &out, const Value &root )
493+
{
494+
document_ = &out;
495+
addChildValues_ = false;
496+
indentString_ = "";
497+
writeCommentBeforeValue( root );
498+
writeValue( root );
499+
writeCommentAfterValueOnSameLine( root );
500+
*document_ << "\n";
501+
document_ = NULL; // Forget the stream, for safety.
502+
}
503+
504+
505+
void
506+
StyledStreamWriter::writeValue( const Value &value )
507+
{
508+
switch ( value.type() )
509+
{
510+
case nullValue:
511+
pushValue( "null" );
512+
break;
513+
case intValue:
514+
pushValue( valueToString( value.asInt() ) );
515+
break;
516+
case uintValue:
517+
pushValue( valueToString( value.asUInt() ) );
518+
break;
519+
case realValue:
520+
pushValue( valueToString( value.asDouble() ) );
521+
break;
522+
case stringValue:
523+
pushValue( valueToQuotedString( value.asCString() ) );
524+
break;
525+
case booleanValue:
526+
pushValue( valueToString( value.asBool() ) );
527+
break;
528+
case arrayValue:
529+
writeArrayValue( value);
530+
break;
531+
case objectValue:
532+
{
533+
Value::Members members( value.getMemberNames() );
534+
if ( members.empty() )
535+
pushValue( "{}" );
536+
else
537+
{
538+
writeWithIndent( "{" );
539+
indent();
540+
Value::Members::iterator it = members.begin();
541+
while ( true )
542+
{
543+
const std::string &name = *it;
544+
const Value &childValue = value[name];
545+
writeCommentBeforeValue( childValue );
546+
writeWithIndent( valueToQuotedString( name.c_str() ) );
547+
*document_ << " : ";
548+
writeValue( childValue );
549+
if ( ++it == members.end() )
550+
{
551+
writeCommentAfterValueOnSameLine( childValue );
552+
break;
553+
}
554+
*document_ << ",";
555+
writeCommentAfterValueOnSameLine( childValue );
556+
}
557+
unindent();
558+
writeWithIndent( "}" );
559+
}
560+
}
561+
break;
562+
}
563+
}
564+
565+
566+
void
567+
StyledStreamWriter::writeArrayValue( const Value &value )
568+
{
569+
unsigned size = value.size();
570+
if ( size == 0 )
571+
pushValue( "[]" );
572+
else
573+
{
574+
bool isArrayMultiLine = isMultineArray( value );
575+
if ( isArrayMultiLine )
576+
{
577+
writeWithIndent( "[" );
578+
indent();
579+
bool hasChildValue = !childValues_.empty();
580+
unsigned index =0;
581+
while ( true )
582+
{
583+
const Value &childValue = value[index];
584+
writeCommentBeforeValue( childValue );
585+
if ( hasChildValue )
586+
writeWithIndent( childValues_[index] );
587+
else
588+
{
589+
writeIndent();
590+
writeValue( childValue );
591+
}
592+
if ( ++index == size )
593+
{
594+
writeCommentAfterValueOnSameLine( childValue );
595+
break;
596+
}
597+
*document_ << ",";
598+
writeCommentAfterValueOnSameLine( childValue );
599+
}
600+
unindent();
601+
writeWithIndent( "]" );
602+
}
603+
else // output on a single line
604+
{
605+
assert( childValues_.size() == size );
606+
*document_ << "[ ";
607+
for ( unsigned index =0; index < size; ++index )
608+
{
609+
if ( index > 0 )
610+
*document_ << ", ";
611+
*document_ << childValues_[index];
612+
}
613+
*document_ << " ]";
614+
}
615+
}
616+
}
617+
618+
619+
bool
620+
StyledStreamWriter::isMultineArray( const Value &value )
621+
{
622+
int size = value.size();
623+
bool isMultiLine = size*3 >= rightMargin_ ;
624+
childValues_.clear();
625+
for ( int index =0; index < size && !isMultiLine; ++index )
626+
{
627+
const Value &childValue = value[index];
628+
isMultiLine = isMultiLine ||
629+
( (childValue.isArray() || childValue.isObject()) &&
630+
childValue.size() > 0 );
631+
}
632+
if ( !isMultiLine ) // check if line length > max line length
633+
{
634+
childValues_.reserve( size );
635+
addChildValues_ = true;
636+
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
637+
for ( int index =0; index < size && !isMultiLine; ++index )
638+
{
639+
writeValue( value[index] );
640+
lineLength += int( childValues_[index].length() );
641+
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
642+
}
643+
addChildValues_ = false;
644+
isMultiLine = isMultiLine || lineLength >= rightMargin_;
645+
}
646+
return isMultiLine;
647+
}
648+
649+
650+
void
651+
StyledStreamWriter::pushValue( const std::string &value )
652+
{
653+
if ( addChildValues_ )
654+
childValues_.push_back( value );
655+
else
656+
*document_ << value;
657+
}
658+
659+
660+
void
661+
StyledStreamWriter::writeIndent()
662+
{
663+
/*
664+
Some comments in this method would have been nice. ;-)
665+
666+
if ( !document_.empty() )
667+
{
668+
char last = document_[document_.length()-1];
669+
if ( last == ' ' ) // already indented
670+
return;
671+
if ( last != '\n' ) // Comments may add new-line
672+
*document_ << '\n';
673+
}
674+
*/
675+
*document_ << indentString_;
676+
}
677+
678+
679+
void
680+
StyledStreamWriter::writeWithIndent( const std::string &value )
681+
{
682+
writeIndent();
683+
*document_ << value;
684+
}
685+
686+
687+
void
688+
StyledStreamWriter::indent()
689+
{
690+
indentString_ += indentation_;
691+
}
692+
693+
694+
void
695+
StyledStreamWriter::unindent()
696+
{
697+
assert( indentString_.size() >= indentation_.size() );
698+
indentString_.resize( indentString_.size() - indentation_.size() );
699+
}
700+
701+
702+
void
703+
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
704+
{
705+
if ( !root.hasComment( commentBefore ) )
706+
return;
707+
*document_ << normalizeEOL( root.getComment( commentBefore ) );
708+
*document_ << "\n";
709+
}
710+
711+
712+
void
713+
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
714+
{
715+
if ( root.hasComment( commentAfterOnSameLine ) )
716+
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
717+
718+
if ( root.hasComment( commentAfter ) )
719+
{
720+
*document_ << "\n";
721+
*document_ << normalizeEOL( root.getComment( commentAfter ) );
722+
*document_ << "\n";
723+
}
724+
}
725+
726+
727+
bool
728+
StyledStreamWriter::hasCommentForValue( const Value &value )
729+
{
730+
return value.hasComment( commentBefore )
731+
|| value.hasComment( commentAfterOnSameLine )
732+
|| value.hasComment( commentAfter );
733+
}
734+
735+
736+
std::string
737+
StyledStreamWriter::normalizeEOL( const std::string &text )
738+
{
739+
std::string normalized;
740+
normalized.reserve( text.length() );
741+
const char *begin = text.c_str();
742+
const char *end = begin + text.length();
743+
const char *current = begin;
744+
while ( current != end )
745+
{
746+
char c = *current++;
747+
if ( c == '\r' ) // mac or dos EOL
748+
{
749+
if ( *current == '\n' ) // convert dos EOL
750+
++current;
751+
normalized += '\n';
752+
}
753+
else // handle unix EOL & other char
754+
normalized += c;
755+
}
756+
return normalized;
757+
}
758+
759+
479760
std::ostream& operator<<( std::ostream &sout, const Value &root )
480761
{
481-
Json::StyledWriter writer;
482-
sout << writer.write(root);
762+
Json::StyledStreamWriter writer;
763+
writer.write(sout, root);
483764
return sout;
484765
}
485766

0 commit comments

Comments
 (0)