mirror of
https://github.com/open-source-parsers/jsoncpp.git
synced 2024-12-13 10:22:55 +01:00
Added StyledStreamWriter, which has no reason to derive from Writer, since its write() method does cannot return a string and must take a stream.
This commit is contained in:
parent
8985cee674
commit
605cd7e902
@ -30,6 +30,7 @@ namespace Json {
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FastWriter();
|
FastWriter();
|
||||||
|
virtual ~FastWriter(){}
|
||||||
|
|
||||||
void enableYAMLCompatibility();
|
void enableYAMLCompatibility();
|
||||||
|
|
||||||
@ -61,7 +62,7 @@ namespace Json {
|
|||||||
*
|
*
|
||||||
* \sa Reader, Value, Value::setComment()
|
* \sa Reader, Value, Value::setComment()
|
||||||
*/
|
*/
|
||||||
class JSON_API StyledWriter
|
class JSON_API StyledWriter: public Writer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
StyledWriter();
|
StyledWriter();
|
||||||
@ -98,13 +99,71 @@ namespace Json {
|
|||||||
bool addChildValues_;
|
bool addChildValues_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a human friendly way,
|
||||||
|
to a stream rather than to a string.
|
||||||
|
*
|
||||||
|
* The rules for line break and indent are as follow:
|
||||||
|
* - Object value:
|
||||||
|
* - if empty then print {} without indent and line break
|
||||||
|
* - if not empty the print '{', line break & indent, print one value per line
|
||||||
|
* and then unindent and line break and print '}'.
|
||||||
|
* - Array value:
|
||||||
|
* - if empty then print [] without indent and line break
|
||||||
|
* - if the array contains no object value, empty array or some other value types,
|
||||||
|
* and all the values fit on one lines, then print the array on a single line.
|
||||||
|
* - otherwise, it the values do not fit on one line, or the array contains
|
||||||
|
* object or non empty array, then print one value per line.
|
||||||
|
*
|
||||||
|
* If the Value have comments then they are outputed according to their #CommentPlacement.
|
||||||
|
*
|
||||||
|
* \param indentation Each level will be indented by this amount extra.
|
||||||
|
* \sa Reader, Value, Value::setComment()
|
||||||
|
*/
|
||||||
|
class JSON_API StyledStreamWriter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StyledStreamWriter( std::string indentation="\t" );
|
||||||
|
~StyledStreamWriter(){}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
|
||||||
|
* \param out Stream to write to. (Can be ostringstream, e.g.)
|
||||||
|
* \param root Value to serialize.
|
||||||
|
* \note There is no point in deriving from Writer, since write() should not return a value.
|
||||||
|
*/
|
||||||
|
void write( std::ostream &out, const Value &root );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void writeValue( const Value &value );
|
||||||
|
void writeArrayValue( const Value &value );
|
||||||
|
bool isMultineArray( const Value &value );
|
||||||
|
void pushValue( const std::string &value );
|
||||||
|
void writeIndent();
|
||||||
|
void writeWithIndent( const std::string &value );
|
||||||
|
void indent();
|
||||||
|
void unindent();
|
||||||
|
void writeCommentBeforeValue( const Value &root );
|
||||||
|
void writeCommentAfterValueOnSameLine( const Value &root );
|
||||||
|
bool hasCommentForValue( const Value &value );
|
||||||
|
static std::string normalizeEOL( const std::string &text );
|
||||||
|
|
||||||
|
typedef std::vector<std::string> ChildValues;
|
||||||
|
|
||||||
|
ChildValues childValues_;
|
||||||
|
std::ostream* document_;
|
||||||
|
std::string indentString_;
|
||||||
|
int rightMargin_;
|
||||||
|
std::string indentation_;
|
||||||
|
bool addChildValues_;
|
||||||
|
};
|
||||||
|
|
||||||
std::string JSON_API valueToString( Value::Int value );
|
std::string JSON_API valueToString( Value::Int value );
|
||||||
std::string JSON_API valueToString( Value::UInt value );
|
std::string JSON_API valueToString( Value::UInt value );
|
||||||
std::string JSON_API valueToString( double value );
|
std::string JSON_API valueToString( double value );
|
||||||
std::string JSON_API valueToString( bool value );
|
std::string JSON_API valueToString( bool value );
|
||||||
std::string JSON_API valueToQuotedString( const char *value );
|
std::string JSON_API valueToQuotedString( const char *value );
|
||||||
|
|
||||||
/// \brief Output using the StyledWriter.
|
/// \brief Output using the StyledStreamWriter.
|
||||||
/// \see Json::operator>>()
|
/// \see Json::operator>>()
|
||||||
std::ostream& operator<<( std::ostream&, const Value &root );
|
std::ostream& operator<<( std::ostream&, const Value &root );
|
||||||
|
|
||||||
|
@ -476,10 +476,291 @@ StyledWriter::normalizeEOL( const std::string &text )
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Class StyledStreamWriter
|
||||||
|
// //////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
StyledStreamWriter::StyledStreamWriter( std::string indentation )
|
||||||
|
: document_(NULL)
|
||||||
|
, rightMargin_( 74 )
|
||||||
|
, indentation_( indentation )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::write( std::ostream &out, const Value &root )
|
||||||
|
{
|
||||||
|
document_ = &out;
|
||||||
|
addChildValues_ = false;
|
||||||
|
indentString_ = "";
|
||||||
|
writeCommentBeforeValue( root );
|
||||||
|
writeValue( root );
|
||||||
|
writeCommentAfterValueOnSameLine( root );
|
||||||
|
*document_ << "\n";
|
||||||
|
document_ = NULL; // Forget the stream, for safety.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeValue( const Value &value )
|
||||||
|
{
|
||||||
|
switch ( value.type() )
|
||||||
|
{
|
||||||
|
case nullValue:
|
||||||
|
pushValue( "null" );
|
||||||
|
break;
|
||||||
|
case intValue:
|
||||||
|
pushValue( valueToString( value.asInt() ) );
|
||||||
|
break;
|
||||||
|
case uintValue:
|
||||||
|
pushValue( valueToString( value.asUInt() ) );
|
||||||
|
break;
|
||||||
|
case realValue:
|
||||||
|
pushValue( valueToString( value.asDouble() ) );
|
||||||
|
break;
|
||||||
|
case stringValue:
|
||||||
|
pushValue( valueToQuotedString( value.asCString() ) );
|
||||||
|
break;
|
||||||
|
case booleanValue:
|
||||||
|
pushValue( valueToString( value.asBool() ) );
|
||||||
|
break;
|
||||||
|
case arrayValue:
|
||||||
|
writeArrayValue( value);
|
||||||
|
break;
|
||||||
|
case objectValue:
|
||||||
|
{
|
||||||
|
Value::Members members( value.getMemberNames() );
|
||||||
|
if ( members.empty() )
|
||||||
|
pushValue( "{}" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeWithIndent( "{" );
|
||||||
|
indent();
|
||||||
|
Value::Members::iterator it = members.begin();
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
const std::string &name = *it;
|
||||||
|
const Value &childValue = value[name];
|
||||||
|
writeCommentBeforeValue( childValue );
|
||||||
|
writeWithIndent( valueToQuotedString( name.c_str() ) );
|
||||||
|
*document_ << " : ";
|
||||||
|
writeValue( childValue );
|
||||||
|
if ( ++it == members.end() )
|
||||||
|
{
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*document_ << ",";
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent( "}" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeArrayValue( const Value &value )
|
||||||
|
{
|
||||||
|
unsigned size = value.size();
|
||||||
|
if ( size == 0 )
|
||||||
|
pushValue( "[]" );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool isArrayMultiLine = isMultineArray( value );
|
||||||
|
if ( isArrayMultiLine )
|
||||||
|
{
|
||||||
|
writeWithIndent( "[" );
|
||||||
|
indent();
|
||||||
|
bool hasChildValue = !childValues_.empty();
|
||||||
|
unsigned index =0;
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
const Value &childValue = value[index];
|
||||||
|
writeCommentBeforeValue( childValue );
|
||||||
|
if ( hasChildValue )
|
||||||
|
writeWithIndent( childValues_[index] );
|
||||||
|
else
|
||||||
|
{
|
||||||
|
writeIndent();
|
||||||
|
writeValue( childValue );
|
||||||
|
}
|
||||||
|
if ( ++index == size )
|
||||||
|
{
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*document_ << ",";
|
||||||
|
writeCommentAfterValueOnSameLine( childValue );
|
||||||
|
}
|
||||||
|
unindent();
|
||||||
|
writeWithIndent( "]" );
|
||||||
|
}
|
||||||
|
else // output on a single line
|
||||||
|
{
|
||||||
|
assert( childValues_.size() == size );
|
||||||
|
*document_ << "[ ";
|
||||||
|
for ( unsigned index =0; index < size; ++index )
|
||||||
|
{
|
||||||
|
if ( index > 0 )
|
||||||
|
*document_ << ", ";
|
||||||
|
*document_ << childValues_[index];
|
||||||
|
}
|
||||||
|
*document_ << " ]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
StyledStreamWriter::isMultineArray( const Value &value )
|
||||||
|
{
|
||||||
|
int size = value.size();
|
||||||
|
bool isMultiLine = size*3 >= rightMargin_ ;
|
||||||
|
childValues_.clear();
|
||||||
|
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||||
|
{
|
||||||
|
const Value &childValue = value[index];
|
||||||
|
isMultiLine = isMultiLine ||
|
||||||
|
( (childValue.isArray() || childValue.isObject()) &&
|
||||||
|
childValue.size() > 0 );
|
||||||
|
}
|
||||||
|
if ( !isMultiLine ) // check if line length > max line length
|
||||||
|
{
|
||||||
|
childValues_.reserve( size );
|
||||||
|
addChildValues_ = true;
|
||||||
|
int lineLength = 4 + (size-1)*2; // '[ ' + ', '*n + ' ]'
|
||||||
|
for ( int index =0; index < size && !isMultiLine; ++index )
|
||||||
|
{
|
||||||
|
writeValue( value[index] );
|
||||||
|
lineLength += int( childValues_[index].length() );
|
||||||
|
isMultiLine = isMultiLine && hasCommentForValue( value[index] );
|
||||||
|
}
|
||||||
|
addChildValues_ = false;
|
||||||
|
isMultiLine = isMultiLine || lineLength >= rightMargin_;
|
||||||
|
}
|
||||||
|
return isMultiLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::pushValue( const std::string &value )
|
||||||
|
{
|
||||||
|
if ( addChildValues_ )
|
||||||
|
childValues_.push_back( value );
|
||||||
|
else
|
||||||
|
*document_ << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeIndent()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Some comments in this method would have been nice. ;-)
|
||||||
|
|
||||||
|
if ( !document_.empty() )
|
||||||
|
{
|
||||||
|
char last = document_[document_.length()-1];
|
||||||
|
if ( last == ' ' ) // already indented
|
||||||
|
return;
|
||||||
|
if ( last != '\n' ) // Comments may add new-line
|
||||||
|
*document_ << '\n';
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
*document_ << indentString_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeWithIndent( const std::string &value )
|
||||||
|
{
|
||||||
|
writeIndent();
|
||||||
|
*document_ << value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::indent()
|
||||||
|
{
|
||||||
|
indentString_ += indentation_;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::unindent()
|
||||||
|
{
|
||||||
|
assert( indentString_.size() >= indentation_.size() );
|
||||||
|
indentString_.resize( indentString_.size() - indentation_.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeCommentBeforeValue( const Value &root )
|
||||||
|
{
|
||||||
|
if ( !root.hasComment( commentBefore ) )
|
||||||
|
return;
|
||||||
|
*document_ << normalizeEOL( root.getComment( commentBefore ) );
|
||||||
|
*document_ << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
StyledStreamWriter::writeCommentAfterValueOnSameLine( const Value &root )
|
||||||
|
{
|
||||||
|
if ( root.hasComment( commentAfterOnSameLine ) )
|
||||||
|
*document_ << " " + normalizeEOL( root.getComment( commentAfterOnSameLine ) );
|
||||||
|
|
||||||
|
if ( root.hasComment( commentAfter ) )
|
||||||
|
{
|
||||||
|
*document_ << "\n";
|
||||||
|
*document_ << normalizeEOL( root.getComment( commentAfter ) );
|
||||||
|
*document_ << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
StyledStreamWriter::hasCommentForValue( const Value &value )
|
||||||
|
{
|
||||||
|
return value.hasComment( commentBefore )
|
||||||
|
|| value.hasComment( commentAfterOnSameLine )
|
||||||
|
|| value.hasComment( commentAfter );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string
|
||||||
|
StyledStreamWriter::normalizeEOL( const std::string &text )
|
||||||
|
{
|
||||||
|
std::string normalized;
|
||||||
|
normalized.reserve( text.length() );
|
||||||
|
const char *begin = text.c_str();
|
||||||
|
const char *end = begin + text.length();
|
||||||
|
const char *current = begin;
|
||||||
|
while ( current != end )
|
||||||
|
{
|
||||||
|
char c = *current++;
|
||||||
|
if ( c == '\r' ) // mac or dos EOL
|
||||||
|
{
|
||||||
|
if ( *current == '\n' ) // convert dos EOL
|
||||||
|
++current;
|
||||||
|
normalized += '\n';
|
||||||
|
}
|
||||||
|
else // handle unix EOL & other char
|
||||||
|
normalized += c;
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::ostream& operator<<( std::ostream &sout, const Value &root )
|
std::ostream& operator<<( std::ostream &sout, const Value &root )
|
||||||
{
|
{
|
||||||
Json::StyledWriter writer;
|
Json::StyledStreamWriter writer;
|
||||||
sout << writer.write(root);
|
writer.write(sout, root);
|
||||||
return sout;
|
return sout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user