@ -170,7 +170,7 @@ namespace Google.Protobuf
continue ;
}
// Omit awkward (single) values such as unknown enum values
if ( ! field . IsRepeated & & ! field . IsMap & & ! CanWriteSingleValue ( accessor . Descriptor , value ) )
if ( ! field . IsRepeated & & ! field . IsMap & & ! CanWriteSingleValue ( value ) )
{
continue ;
}
@ -182,7 +182,7 @@ namespace Google.Protobuf
}
WriteString ( builder , ToCamelCase ( accessor . Descriptor . Name ) ) ;
builder . Append ( ": " ) ;
WriteValue ( builder , accessor , value ) ;
WriteValue ( builder , value ) ;
first = false ;
}
builder . Append ( first ? "}" : " }" ) ;
@ -291,93 +291,81 @@ namespace Google.Protobuf
throw new ArgumentException ( "Invalid field type" ) ;
}
}
private void WriteValue ( StringBuilder builder , IFieldAccessor accessor , object value )
private void WriteValue ( StringBuilder builder , object value )
{
if ( accessor . Descriptor . IsMap )
if ( value = = null )
{
WriteDictionary ( builder , accessor , ( IDictionary ) value ) ;
WriteNull ( builder ) ;
}
else if ( accessor . Descriptor . IsRepeated )
else if ( value is bool )
{
WriteList ( builder , accessor , ( IList ) value ) ;
builder . Append ( ( bool ) value ? "true" : "false" ) ;
}
else
else if ( value is ByteString )
{
WriteSingleValue ( builder , accessor . Descriptor , value ) ;
// Nothing in Base64 needs escaping
builder . Append ( '"' ) ;
builder . Append ( ( ( ByteString ) value ) . ToBase64 ( ) ) ;
builder . Append ( '"' ) ;
}
}
private void WriteSingleValue ( StringBuilder builder , FieldDescriptor descriptor , object value )
{
switch ( descriptor . FieldType )
else if ( value is string )
{
case FieldType . Bool :
builder . Append ( ( bool ) value ? "true" : "false" ) ;
break ;
case FieldType . Bytes :
// Nothing in Base64 needs escaping
WriteString ( builder , ( string ) value ) ;
}
else if ( value is IDictionary )
{
WriteDictionary ( builder , ( IDictionary ) value ) ;
}
else if ( value is IList )
{
WriteList ( builder , ( IList ) value ) ;
}
else if ( value is int | | value is uint )
{
IFormattable formattable = ( IFormattable ) value ;
builder . Append ( formattable . ToString ( "d" , CultureInfo . InvariantCulture ) ) ;
}
else if ( value is long | | value is ulong )
{
builder . Append ( '"' ) ;
IFormattable formattable = ( IFormattable ) value ;
builder . Append ( formattable . ToString ( "d" , CultureInfo . InvariantCulture ) ) ;
builder . Append ( '"' ) ;
}
else if ( value is System . Enum )
{
WriteString ( builder , value . ToString ( ) ) ;
}
else if ( value is float | | value is double )
{
string text = ( ( IFormattable ) value ) . ToString ( "r" , CultureInfo . InvariantCulture ) ;
if ( text = = "NaN" | | text = = "Infinity" | | text = = "-Infinity" )
{
builder . Append ( '"' ) ;
builder . Append ( ( ( ByteString ) value ) . ToBase64 ( ) ) ;
builder . Append ( text ) ;
builder . Append ( '"' ) ;
break ;
case FieldType . String :
WriteString ( builder , ( string ) value ) ;
break ;
case FieldType . Fixed32 :
case FieldType . UInt32 :
case FieldType . SInt32 :
case FieldType . Int32 :
case FieldType . SFixed32 :
{
IFormattable formattable = ( IFormattable ) value ;
builder . Append ( formattable . ToString ( "d" , CultureInfo . InvariantCulture ) ) ;
break ;
}
case FieldType . Enum :
EnumValueDescriptor enumValue = descriptor . EnumType . FindValueByNumber ( ( int ) value ) ;
// We will already have validated that this is a known value.
WriteString ( builder , enumValue . Name ) ;
break ;
case FieldType . Fixed64 :
case FieldType . UInt64 :
case FieldType . SFixed64 :
case FieldType . Int64 :
case FieldType . SInt64 :
{
builder . Append ( '"' ) ;
IFormattable formattable = ( IFormattable ) value ;
builder . Append ( formattable . ToString ( "d" , CultureInfo . InvariantCulture ) ) ;
builder . Append ( '"' ) ;
break ;
}
case FieldType . Double :
case FieldType . Float :
string text = ( ( IFormattable ) value ) . ToString ( "r" , CultureInfo . InvariantCulture ) ;
if ( text = = "NaN" | | text = = "Infinity" | | text = = "-Infinity" )
{
builder . Append ( '"' ) ;
builder . Append ( text ) ;
builder . Append ( '"' ) ;
}
else
{
builder . Append ( text ) ;
}
break ;
case FieldType . Message :
case FieldType . Group : // Never expect to get this, but...
if ( descriptor . MessageType . IsWellKnownType )
{
WriteWellKnownTypeValue ( builder , descriptor . MessageType , value , true ) ;
}
else
{
WriteMessage ( builder , ( IMessage ) value ) ;
}
break ;
default :
throw new ArgumentException ( "Invalid field type: " + descriptor . FieldType ) ;
}
else
{
builder . Append ( text ) ;
}
}
else if ( value is IMessage )
{
IMessage message = ( IMessage ) value ;
if ( message . Descriptor . IsWellKnownType )
{
WriteWellKnownTypeValue ( builder , message . Descriptor , value , true ) ;
}
else
{
WriteMessage ( builder , ( IMessage ) value ) ;
}
}
else
{
throw new ArgumentException ( "Unable to format value of type " + value . GetType ( ) ) ;
}
}
@ -398,7 +386,7 @@ namespace Google.Protobuf
// so we can write it as if we were unconditionally writing the Value field for the wrapper type.
if ( descriptor . File = = Int32Value . Descriptor . File )
{
WriteSingle Value ( builder , descriptor . FindFieldByNumber ( 1 ) , value ) ;
WriteValue ( builder , value ) ;
return ;
}
if ( descriptor . FullName = = Timestamp . Descriptor . FullName )
@ -424,7 +412,7 @@ namespace Google.Protobuf
if ( descriptor . FullName = = ListValue . Descriptor . FullName )
{
var fieldAccessor = descriptor . Fields [ ListValue . ValuesFieldNumber ] . Accessor ;
WriteList ( builder , fieldAccessor , ( IList ) fieldAccessor . GetValue ( ( IMessage ) value ) ) ;
WriteList ( builder , ( IList ) fieldAccessor . GetValue ( ( IMessage ) value ) ) ;
return ;
}
if ( descriptor . FullName = = Value . Descriptor . FullName )
@ -565,7 +553,7 @@ namespace Google.Protobuf
case Value . BoolValueFieldNumber :
case Value . StringValueFieldNumber :
case Value . NumberValueFieldNumber :
WriteSingle Value ( builder , specifiedField , value ) ;
WriteValue ( builder , value ) ;
return ;
case Value . StructValueFieldNumber :
case Value . ListValueFieldNumber :
@ -581,13 +569,13 @@ namespace Google.Protobuf
}
}
private void WriteList ( StringBuilder builder , IFieldAccessor accesso r , IList list )
internal void WriteList ( StringBuilder builder , IList list )
{
builder . Append ( "[ " ) ;
bool first = true ;
foreach ( var value in list )
{
if ( ! CanWriteSingleValue ( accessor . Descriptor , value ) )
if ( ! CanWriteSingleValue ( value ) )
{
continue ;
}
@ -595,22 +583,20 @@ namespace Google.Protobuf
{
builder . Append ( ", " ) ;
}
WriteSingle Value ( builder , accessor . Descripto r , value ) ;
WriteValue ( builder , value ) ;
first = false ;
}
builder . Append ( first ? "]" : " ]" ) ;
}
private void WriteDictionary ( StringBuilder builder , IFieldAccessor accesso r , IDictionary dictionary )
internal void WriteDictionary ( StringBuilder builder , IDictionary dictionary )
{
builder . Append ( "{ " ) ;
bool first = true ;
FieldDescriptor keyType = accessor . Descriptor . MessageType . FindFieldByNumber ( 1 ) ;
FieldDescriptor valueType = accessor . Descriptor . MessageType . FindFieldByNumber ( 2 ) ;
// This will box each pair. Could use IDictionaryEnumerator, but that's ugly in terms of disposal.
foreach ( DictionaryEntry pair in dictionary )
{
if ( ! CanWriteSingleValue ( valueType , pair . Value ) )
if ( ! CanWriteSingleValue ( pair . Value ) )
{
continue ;
}
@ -619,32 +605,29 @@ namespace Google.Protobuf
builder . Append ( ", " ) ;
}
string keyText ;
switch ( keyType . FieldType )
if ( pair . Key is string )
{
case FieldType . String :
keyText = ( string ) pair . Key ;
break ;
case FieldType . Bool :
keyText = ( bool ) pair . Key ? "true" : "false" ;
break ;
case FieldType . Fixed32 :
case FieldType . Fixed64 :
case FieldType . SFixed32 :
case FieldType . SFixed64 :
case FieldType . Int32 :
case FieldType . Int64 :
case FieldType . SInt32 :
case FieldType . SInt64 :
case FieldType . UInt32 :
case FieldType . UInt64 :
keyText = ( ( IFormattable ) pair . Key ) . ToString ( "d" , CultureInfo . InvariantCulture ) ;
break ;
default :
throw new ArgumentException ( "Invalid key type: " + keyType . FieldType ) ;
keyText = ( string ) pair . Key ;
}
else if ( pair . Key is bool )
{
keyText = ( bool ) pair . Key ? "true" : "false" ;
}
else if ( pair . Key is int | | pair . Key is uint | pair . Key is long | | pair . Key is ulong )
{
keyText = ( ( IFormattable ) pair . Key ) . ToString ( "d" , CultureInfo . InvariantCulture ) ;
}
else
{
if ( pair . Key = = null )
{
throw new ArgumentException ( "Dictionary has entry with null key" ) ;
}
throw new ArgumentException ( "Unhandled dictionary key type: " + pair . Key . GetType ( ) ) ;
}
WriteString ( builder , keyText ) ;
builder . Append ( ": " ) ;
WriteSingle Value ( builder , valueType , pair . Value ) ;
WriteValue ( builder , pair . Value ) ;
first = false ;
}
builder . Append ( first ? "}" : " }" ) ;
@ -655,12 +638,11 @@ namespace Google.Protobuf
/// Currently only relevant for enums, where unknown values can't be represented.
/// For repeated/map fields, this always returns true.
/// </summary>
private bool CanWriteSingleValue ( FieldDescriptor descriptor , object value )
private bool CanWriteSingleValue ( object value )
{
if ( descriptor . FieldType = = FieldType . Enum )
if ( value is System . Enum )
{
EnumValueDescriptor enumValue = descriptor . EnumType . FindValueByNumber ( ( int ) value ) ;
return enumValue ! = null ;
return System . Enum . IsDefined ( value . GetType ( ) , value ) ;
}
return true ;
}