aboutsummaryrefslogtreecommitdiffhomepage
path: root/csharp/src/Google.Protobuf/JsonFormatter.cs
diff options
context:
space:
mode:
authorGravatar Jon Skeet <jonskeet@google.com>2015-12-15 09:23:38 +0000
committerGravatar Jon Skeet <jonskeet@google.com>2015-12-15 09:23:38 +0000
commitaabc6c411a4ae2c5b63124d4079ea9b0dc0879c7 (patch)
tree2b82f7c54db9fe91944750650c7704e8fa66ed47 /csharp/src/Google.Protobuf/JsonFormatter.cs
parentdc633398af5c87e1fe1ffcedaebd697c4775c918 (diff)
Make ToString() valid without a type registry
This addresses issue #1008, by creating a JsonFormatter which is private and only different to JsonFormatter.Default in terms of reference equality. Other plausible designs: - The same, but expose the diagnostic-only formatter - Add something to settings to say "I don't have a type registry at all" - Change the behaviour of JsonFormatter.Default (bad idea IMO, as we really *don't* want the result of this used as regular JSON to be parsed) Note that just trying to find a separate fix to issue #933 and using that to override Any.ToString() differently wouldn't work for messages that *contain* an Any. Generated code changes follow in the next commit.
Diffstat (limited to 'csharp/src/Google.Protobuf/JsonFormatter.cs')
-rw-r--r--csharp/src/Google.Protobuf/JsonFormatter.cs54
1 files changed, 51 insertions, 3 deletions
diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs
index 45941b39..7b99f314 100644
--- a/csharp/src/Google.Protobuf/JsonFormatter.cs
+++ b/csharp/src/Google.Protobuf/JsonFormatter.cs
@@ -56,17 +56,19 @@ namespace Google.Protobuf
public sealed class JsonFormatter
{
internal const string AnyTypeUrlField = "@type";
+ internal const string AnyDiagnosticValueField = "@value";
internal const string AnyWellKnownTypeValueField = "value";
private const string TypeUrlPrefix = "type.googleapis.com";
private const string NameValueSeparator = ": ";
private const string PropertySeparator = ", ";
- private static JsonFormatter defaultInstance = new JsonFormatter(Settings.Default);
-
/// <summary>
/// Returns a formatter using the default settings.
/// </summary>
- public static JsonFormatter Default { get { return defaultInstance; } }
+ public static JsonFormatter Default { get; } = new JsonFormatter(Settings.Default);
+
+ // A JSON formatter which *only* exists
+ private static readonly JsonFormatter diagnosticFormatter = new JsonFormatter(Settings.Default);
/// <summary>
/// The JSON representation of the first 160 characters of Unicode.
@@ -149,6 +151,29 @@ namespace Google.Protobuf
return builder.ToString();
}
+ /// <summary>
+ /// Converts a message to JSON for diagnostic purposes with no extra context.
+ /// </summary>
+ /// <remarks>
+ /// <para>
+ /// This differs from calling <see cref="Format(IMessage)"/> on the default JSON
+ /// formatter in its handling of <see cref="Any"/>. As no type registry is available
+ /// in <see cref="object.ToString"/> calls, the normal way of resolving the type of
+ /// an <c>Any</c> message cannot be applied. Instead, a JSON property named <c>@value</c>
+ /// is included with the base64 data from the <see cref="Any.Value"/> property of the message.
+ /// </para>
+ /// <para>The value returned by this method is only designed to be used for diagnostic
+ /// purposes. It may not be parsable by <see cref="JsonParser"/>, and may not be parsable
+ /// by other Protocol Buffer implementations.</para>
+ /// </remarks>
+ /// <param name="message">The message to format for diagnostic purposes.</param>
+ /// <returns>The diagnostic-only JSON representation of the message</returns>
+ public static string ToDiagnosticString(IMessage message)
+ {
+ Preconditions.CheckNotNull(message, nameof(message));
+ return diagnosticFormatter.Format(message);
+ }
+
private void WriteMessage(StringBuilder builder, IMessage message)
{
if (message == null)
@@ -516,6 +541,12 @@ namespace Google.Protobuf
private void WriteAny(StringBuilder builder, IMessage value)
{
+ if (ReferenceEquals(this, diagnosticFormatter))
+ {
+ WriteDiagnosticOnlyAny(builder, value);
+ return;
+ }
+
string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value);
ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value);
string typeName = GetTypeName(typeUrl);
@@ -544,6 +575,23 @@ namespace Google.Protobuf
builder.Append(" }");
}
+ private void WriteDiagnosticOnlyAny(StringBuilder builder, IMessage value)
+ {
+ string typeUrl = (string) value.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.GetValue(value);
+ ByteString data = (ByteString) value.Descriptor.Fields[Any.ValueFieldNumber].Accessor.GetValue(value);
+ builder.Append("{ ");
+ WriteString(builder, AnyTypeUrlField);
+ builder.Append(NameValueSeparator);
+ WriteString(builder, typeUrl);
+ builder.Append(PropertySeparator);
+ WriteString(builder, AnyDiagnosticValueField);
+ builder.Append(NameValueSeparator);
+ builder.Append('"');
+ builder.Append(data.ToBase64());
+ builder.Append('"');
+ builder.Append(" }");
+ }
+
internal static string GetTypeName(String typeUrl)
{
string[] parts = typeUrl.Split('/');