|  | @@ -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)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                WriteSingleValue(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:
 | 
	
		
			
				|  |  | -                    WriteSingleValue(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 accessor, 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(", ");
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                WriteSingleValue(builder, accessor.Descriptor, value);
 | 
	
		
			
				|  |  | +                WriteValue(builder, value);
 | 
	
		
			
				|  |  |                  first = false;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              builder.Append(first ? "]" : " ]");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private void WriteDictionary(StringBuilder builder, IFieldAccessor accessor, 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(": ");
 | 
	
		
			
				|  |  | -                WriteSingleValue(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;
 | 
	
		
			
				|  |  |          }
 |