|  | @@ -136,13 +136,28 @@ namespace Google.Protobuf
 | 
	
		
			
				|  |  |              builder.Append("{ ");
 | 
	
		
			
				|  |  |              var fields = message.Fields;
 | 
	
		
			
				|  |  |              bool first = true;
 | 
	
		
			
				|  |  | +            // First non-oneof fields
 | 
	
		
			
				|  |  |              foreach (var accessor in fields.Accessors)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | +                var descriptor = accessor.Descriptor;
 | 
	
		
			
				|  |  | +                // Oneofs are written later
 | 
	
		
			
				|  |  | +                if (descriptor.ContainingOneof != null)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                // Omit default values unless we're asked to format them
 | 
	
		
			
				|  |  |                  object value = accessor.GetValue(message);
 | 
	
		
			
				|  |  |                  if (!settings.FormatDefaultValues && IsDefaultValue(accessor, value))
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      continue;
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | +                // Omit awkward (single) values such as unknown enum values
 | 
	
		
			
				|  |  | +                if (!descriptor.IsRepeated && !descriptor.IsMap && !CanWriteSingleValue(accessor.Descriptor, value))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                // Okay, all tests complete: let's write the field value...
 | 
	
		
			
				|  |  |                  if (!first)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      builder.Append(", ");
 | 
	
	
		
			
				|  | @@ -152,6 +167,32 @@ namespace Google.Protobuf
 | 
	
		
			
				|  |  |                  WriteValue(builder, accessor, value);
 | 
	
		
			
				|  |  |                  first = false;
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // Now oneofs
 | 
	
		
			
				|  |  | +            foreach (var accessor in fields.Oneofs)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                var fieldDescriptor = accessor.GetCaseFieldDescriptor(message);
 | 
	
		
			
				|  |  | +                if (fieldDescriptor == null)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                var fieldAccessor = fields[fieldDescriptor];
 | 
	
		
			
				|  |  | +                object value = fieldAccessor.GetValue(message);
 | 
	
		
			
				|  |  | +                // Omit awkward (single) values such as unknown enum values
 | 
	
		
			
				|  |  | +                if (!fieldDescriptor.IsRepeated && !fieldDescriptor.IsMap && !CanWriteSingleValue(fieldDescriptor, value))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +                if (!first)
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    builder.Append(", ");
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +                WriteString(builder, ToCamelCase(fieldDescriptor.Name));
 | 
	
		
			
				|  |  | +                builder.Append(": ");
 | 
	
		
			
				|  |  | +                WriteValue(builder, fieldAccessor, value);
 | 
	
		
			
				|  |  | +                first = false;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  |              builder.Append(first ? "}" : " }");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -303,15 +344,8 @@ namespace Google.Protobuf
 | 
	
		
			
				|  |  |                      }
 | 
	
		
			
				|  |  |                  case FieldType.Enum:
 | 
	
		
			
				|  |  |                      EnumValueDescriptor enumValue = descriptor.EnumType.FindValueByNumber((int) value);
 | 
	
		
			
				|  |  | -                    if (enumValue != null)
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        WriteString(builder, enumValue.Name);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                    else
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        // ??? Need more documentation
 | 
	
		
			
				|  |  | -                        builder.Append(((int) value).ToString("d", CultureInfo.InvariantCulture));
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | +                    // We will already have validated that this is a known value.
 | 
	
		
			
				|  |  | +                    WriteString(builder, enumValue.Name);
 | 
	
		
			
				|  |  |                      break;
 | 
	
		
			
				|  |  |                  case FieldType.Fixed64:
 | 
	
		
			
				|  |  |                  case FieldType.UInt64:
 | 
	
	
		
			
				|  | @@ -354,6 +388,10 @@ namespace Google.Protobuf
 | 
	
		
			
				|  |  |              bool first = true;
 | 
	
		
			
				|  |  |              foreach (var value in list)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | +                if (!CanWriteSingleValue(accessor.Descriptor, value))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |                  if (!first)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      builder.Append(", ");
 | 
	
	
		
			
				|  | @@ -373,6 +411,10 @@ namespace Google.Protobuf
 | 
	
		
			
				|  |  |              // 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))
 | 
	
		
			
				|  |  | +                {
 | 
	
		
			
				|  |  | +                    continue;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  |                  if (!first)
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  |                      builder.Append(", ");
 | 
	
	
		
			
				|  | @@ -409,6 +451,21 @@ namespace Google.Protobuf
 | 
	
		
			
				|  |  |              builder.Append(first ? "}" : " }");
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        /// Returns whether or not a singular value can be represented in JSON.
 | 
	
		
			
				|  |  | +        /// 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)
 | 
	
		
			
				|  |  | +        {
 | 
	
		
			
				|  |  | +            if (descriptor.FieldType == FieldType.Enum)
 | 
	
		
			
				|  |  | +            {
 | 
	
		
			
				|  |  | +                EnumValueDescriptor enumValue = descriptor.EnumType.FindValueByNumber((int) value);
 | 
	
		
			
				|  |  | +                return enumValue != null;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            return true;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Writes a string (including leading and trailing double quotes) to a builder, escaping as required.
 | 
	
		
			
				|  |  |          /// </summary>
 |