OLD | NEW |
1 #region Copyright notice and license | 1 #region Copyright notice and license |
2 // Protocol Buffers - Google's data interchange format | 2 // Protocol Buffers - Google's data interchange format |
3 // Copyright 2015 Google Inc. All rights reserved. | 3 // Copyright 2015 Google Inc. All rights reserved. |
4 // https://developers.google.com/protocol-buffers/ | 4 // https://developers.google.com/protocol-buffers/ |
5 // | 5 // |
6 // Redistribution and use in source and binary forms, with or without | 6 // Redistribution and use in source and binary forms, with or without |
7 // modification, are permitted provided that the following conditions are | 7 // modification, are permitted provided that the following conditions are |
8 // met: | 8 // met: |
9 // | 9 // |
10 // * Redistributions of source code must retain the above copyright | 10 // * Redistributions of source code must retain the above copyright |
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 | 241 |
242 WriteString(writer, accessor.Descriptor.JsonName); | 242 WriteString(writer, accessor.Descriptor.JsonName); |
243 writer.Write(NameValueSeparator); | 243 writer.Write(NameValueSeparator); |
244 WriteValue(writer, value); | 244 WriteValue(writer, value); |
245 | 245 |
246 first = false; | 246 first = false; |
247 } | 247 } |
248 return !first; | 248 return !first; |
249 } | 249 } |
250 | 250 |
251 // Converted from java/core/src/main/java/com/google/protobuf/Descriptor
s.java | 251 /// <summary> |
252 internal static string ToJsonName(string name) | 252 /// Camel-case converter with added strictness for field mask formatting
. |
| 253 /// </summary> |
| 254 /// <exception cref="InvalidOperationException">The field mask is invali
d for JSON representation</exception> |
| 255 private static string ToCamelCaseForFieldMask(string input) |
253 { | 256 { |
254 StringBuilder result = new StringBuilder(name.Length); | 257 for (int i = 0; i < input.Length; i++) |
255 bool isNextUpperCase = false; | |
256 foreach (char ch in name) | |
257 { | 258 { |
258 if (ch == '_') | 259 char c = input[i]; |
| 260 if (c >= 'A' && c <= 'Z') |
259 { | 261 { |
260 isNextUpperCase = true; | 262 throw new InvalidOperationException($"Invalid field mask to
be converted to JSON: {input}"); |
261 } | 263 } |
262 else if (isNextUpperCase) | 264 if (c == '_' && i < input.Length - 1) |
263 { | 265 { |
264 result.Append(char.ToUpperInvariant(ch)); | 266 char next = input[i + 1]; |
265 isNextUpperCase = false; | 267 if (next < 'a' || next > 'z') |
| 268 { |
| 269 throw new InvalidOperationException($"Invalid field mask
to be converted to JSON: {input}"); |
| 270 } |
266 } | 271 } |
267 else | 272 } |
| 273 return ToCamelCase(input); |
| 274 } |
| 275 |
| 276 // Converted from src/google/protobuf/util/internal/utility.cc ToCamelCa
se |
| 277 // TODO: Use the new field in FieldDescriptor. |
| 278 internal static string ToCamelCase(string input) |
| 279 { |
| 280 bool capitalizeNext = false; |
| 281 bool wasCap = true; |
| 282 bool isCap = false; |
| 283 bool firstWord = true; |
| 284 StringBuilder result = new StringBuilder(input.Length); |
| 285 |
| 286 for (int i = 0; i < input.Length; i++, wasCap = isCap) |
| 287 { |
| 288 isCap = char.IsUpper(input[i]); |
| 289 if (input[i] == '_') |
268 { | 290 { |
269 result.Append(ch); | 291 capitalizeNext = true; |
| 292 if (result.Length != 0) |
| 293 { |
| 294 firstWord = false; |
| 295 } |
| 296 continue; |
270 } | 297 } |
| 298 else if (firstWord) |
| 299 { |
| 300 // Consider when the current character B is capitalized, |
| 301 // first word ends when: |
| 302 // 1) following a lowercase: "...aB..." |
| 303 // 2) followed by a lowercase: "...ABc..." |
| 304 if (result.Length != 0 && isCap && |
| 305 (!wasCap || (i + 1 < input.Length && char.IsLower(input[
i + 1])))) |
| 306 { |
| 307 firstWord = false; |
| 308 } |
| 309 else |
| 310 { |
| 311 result.Append(char.ToLowerInvariant(input[i])); |
| 312 continue; |
| 313 } |
| 314 } |
| 315 else if (capitalizeNext) |
| 316 { |
| 317 capitalizeNext = false; |
| 318 if (char.IsLower(input[i])) |
| 319 { |
| 320 result.Append(char.ToUpperInvariant(input[i])); |
| 321 continue; |
| 322 } |
| 323 } |
| 324 result.Append(input[i]); |
271 } | 325 } |
272 return result.ToString(); | 326 return result.ToString(); |
273 } | 327 } |
274 | 328 |
275 private static void WriteNull(TextWriter writer) | 329 private static void WriteNull(TextWriter writer) |
276 { | 330 { |
277 writer.Write("null"); | 331 writer.Write("null"); |
278 } | 332 } |
279 | 333 |
280 private static bool IsDefaultValue(IFieldAccessor accessor, object value
) | 334 private static bool IsDefaultValue(IFieldAccessor accessor, object value
) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 return (long) value == 0; | 370 return (long) value == 0; |
317 case FieldType.Float: | 371 case FieldType.Float: |
318 return (float) value == 0f; | 372 return (float) value == 0f; |
319 case FieldType.Message: | 373 case FieldType.Message: |
320 case FieldType.Group: // Never expect to get this, but... | 374 case FieldType.Group: // Never expect to get this, but... |
321 return value == null; | 375 return value == null; |
322 default: | 376 default: |
323 throw new ArgumentException("Invalid field type"); | 377 throw new ArgumentException("Invalid field type"); |
324 } | 378 } |
325 } | 379 } |
326 | 380 |
327 /// <summary> | 381 private void WriteValue(TextWriter writer, object value) |
328 /// Writes a single value to the given writer as JSON. Only types unders
tood by | |
329 /// Protocol Buffers can be written in this way. This method is only exp
osed for | |
330 /// advanced use cases; most users should be using <see cref="Format(IMe
ssage)"/> | |
331 /// or <see cref="Format(IMessage, TextWriter)"/>. | |
332 /// </summary> | |
333 /// <param name="writer">The writer to write the value to. Must not be n
ull.</param> | |
334 /// <param name="value">The value to write. May be null.</param> | |
335 public void WriteValue(TextWriter writer, object value) | |
336 { | 382 { |
337 if (value == null) | 383 if (value == null) |
338 { | 384 { |
339 WriteNull(writer); | 385 WriteNull(writer); |
340 } | 386 } |
341 else if (value is bool) | 387 else if (value is bool) |
342 { | 388 { |
343 writer.Write((bool)value ? "true" : "false"); | 389 writer.Write((bool)value ? "true" : "false"); |
344 } | 390 } |
345 else if (value is ByteString) | 391 else if (value is ByteString) |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
394 writer.Write(text); | 440 writer.Write(text); |
395 writer.Write('"'); | 441 writer.Write('"'); |
396 } | 442 } |
397 else | 443 else |
398 { | 444 { |
399 writer.Write(text); | 445 writer.Write(text); |
400 } | 446 } |
401 } | 447 } |
402 else if (value is IMessage) | 448 else if (value is IMessage) |
403 { | 449 { |
404 Format((IMessage)value, writer); | 450 IMessage message = (IMessage) value; |
| 451 if (message.Descriptor.IsWellKnownType) |
| 452 { |
| 453 WriteWellKnownTypeValue(writer, message.Descriptor, value); |
| 454 } |
| 455 else |
| 456 { |
| 457 WriteMessage(writer, (IMessage)value); |
| 458 } |
405 } | 459 } |
406 else | 460 else |
407 { | 461 { |
408 throw new ArgumentException("Unable to format value of type " +
value.GetType()); | 462 throw new ArgumentException("Unable to format value of type " +
value.GetType()); |
409 } | 463 } |
410 } | 464 } |
411 | 465 |
412 /// <summary> | 466 /// <summary> |
413 /// Central interception point for well-known type formatting. Any well-
known types which | 467 /// Central interception point for well-known type formatting. Any well-
known types which |
414 /// don't need special handling can fall back to WriteMessage. We avoid
assuming that the | 468 /// don't need special handling can fall back to WriteMessage. We avoid
assuming that the |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 } | 717 } |
664 WriteString(writer, keyText); | 718 WriteString(writer, keyText); |
665 writer.Write(NameValueSeparator); | 719 writer.Write(NameValueSeparator); |
666 WriteValue(writer, pair.Value); | 720 WriteValue(writer, pair.Value); |
667 first = false; | 721 first = false; |
668 } | 722 } |
669 writer.Write(first ? "}" : " }"); | 723 writer.Write(first ? "}" : " }"); |
670 } | 724 } |
671 | 725 |
672 /// <summary> | 726 /// <summary> |
| 727 /// Returns whether or not a singular value can be represented in JSON. |
| 728 /// Currently only relevant for enums, where unknown values can't be rep
resented. |
| 729 /// For repeated/map fields, this always returns true. |
| 730 /// </summary> |
| 731 private bool CanWriteSingleValue(object value) |
| 732 { |
| 733 if (value is System.Enum) |
| 734 { |
| 735 return System.Enum.IsDefined(value.GetType(), value); |
| 736 } |
| 737 return true; |
| 738 } |
| 739 |
| 740 /// <summary> |
673 /// Writes a string (including leading and trailing double quotes) to a
builder, escaping as required. | 741 /// Writes a string (including leading and trailing double quotes) to a
builder, escaping as required. |
674 /// </summary> | 742 /// </summary> |
675 /// <remarks> | 743 /// <remarks> |
676 /// Other than surrogate pair handling, this code is mostly taken from s
rc/google/protobuf/util/internal/json_escaping.cc. | 744 /// Other than surrogate pair handling, this code is mostly taken from s
rc/google/protobuf/util/internal/json_escaping.cc. |
677 /// </remarks> | 745 /// </remarks> |
678 internal static void WriteString(TextWriter writer, string text) | 746 internal static void WriteString(TextWriter writer, string text) |
679 { | 747 { |
680 writer.Write('"'); | 748 writer.Write('"'); |
681 for (int i = 0; i < text.Length; i++) | 749 for (int i = 0; i < text.Length; i++) |
682 { | 750 { |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
824 dictionaries[enumType] = nameMapping; | 892 dictionaries[enumType] = nameMapping; |
825 } | 893 } |
826 } | 894 } |
827 | 895 |
828 string originalName; | 896 string originalName; |
829 // If this returns false, originalName will be null, which is wh
at we want. | 897 // If this returns false, originalName will be null, which is wh
at we want. |
830 nameMapping.TryGetValue(value, out originalName); | 898 nameMapping.TryGetValue(value, out originalName); |
831 return originalName; | 899 return originalName; |
832 } | 900 } |
833 | 901 |
834 #if DOTNET35 | |
835 // TODO: Consider adding functionality to TypeExtensions to avoid th
is difference. | |
836 private static Dictionary<object, string> GetNameMapping(System.Type
enumType) => | |
837 enumType.GetFields(BindingFlags.NonPublic | BindingFlags.Public
| BindingFlags.Static) | |
838 .ToDictionary(f => f.GetValue(null), | |
839 f => (f.GetCustomAttributes(typeof(OriginalNam
eAttribute), false) | |
840 .FirstOrDefault() as OriginalNameAttribu
te) | |
841 // If the attribute hasn't been applied,
fall back to the name of the field. | |
842 ?.Name ?? f.Name); | |
843 #else | |
844 private static Dictionary<object, string> GetNameMapping(System.Type
enumType) => | 902 private static Dictionary<object, string> GetNameMapping(System.Type
enumType) => |
845 enumType.GetTypeInfo().DeclaredFields | 903 enumType.GetTypeInfo().DeclaredFields |
846 .Where(f => f.IsStatic) | 904 .Where(f => f.IsStatic) |
847 .ToDictionary(f => f.GetValue(null), | 905 .ToDictionary(f => f.GetValue(null), |
848 f => f.GetCustomAttributes<OriginalNameAttribu
te>() | 906 f => f.GetCustomAttributes<OriginalNameAttribu
te>() |
849 .FirstOrDefault() | 907 .FirstOrDefault() |
850 // If the attribute hasn't been applied,
fall back to the name of the field. | 908 // If the attribute hasn't been applied,
fall back to the name of the field. |
851 ?.Name ?? f.Name); | 909 ?.Name ?? f.Name); |
852 #endif | |
853 } | 910 } |
854 } | 911 } |
855 } | 912 } |
OLD | NEW |