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 /// <summary> | 251 // Converted from java/core/src/main/java/com/google/protobuf/Descriptor
s.java |
252 /// Camel-case converter with added strictness for field mask formatting
. | 252 internal static string ToJsonName(string name) |
253 /// </summary> | |
254 /// <exception cref="InvalidOperationException">The field mask is invali
d for JSON representation</exception> | |
255 private static string ToCamelCaseForFieldMask(string input) | |
256 { | 253 { |
257 for (int i = 0; i < input.Length; i++) | 254 StringBuilder result = new StringBuilder(name.Length); |
| 255 bool isNextUpperCase = false; |
| 256 foreach (char ch in name) |
258 { | 257 { |
259 char c = input[i]; | 258 if (ch == '_') |
260 if (c >= 'A' && c <= 'Z') | |
261 { | 259 { |
262 throw new InvalidOperationException($"Invalid field mask to
be converted to JSON: {input}"); | 260 isNextUpperCase = true; |
263 } | 261 } |
264 if (c == '_' && i < input.Length - 1) | 262 else if (isNextUpperCase) |
265 { | 263 { |
266 char next = input[i + 1]; | 264 result.Append(char.ToUpperInvariant(ch)); |
267 if (next < 'a' || next > 'z') | 265 isNextUpperCase = false; |
268 { | |
269 throw new InvalidOperationException($"Invalid field mask
to be converted to JSON: {input}"); | |
270 } | |
271 } | 266 } |
272 } | 267 else |
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] == '_') | |
290 { | 268 { |
291 capitalizeNext = true; | 269 result.Append(ch); |
292 if (result.Length != 0) | |
293 { | |
294 firstWord = false; | |
295 } | |
296 continue; | |
297 } | 270 } |
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]); | |
325 } | 271 } |
326 return result.ToString(); | 272 return result.ToString(); |
327 } | 273 } |
328 | 274 |
329 private static void WriteNull(TextWriter writer) | 275 private static void WriteNull(TextWriter writer) |
330 { | 276 { |
331 writer.Write("null"); | 277 writer.Write("null"); |
332 } | 278 } |
333 | 279 |
334 private static bool IsDefaultValue(IFieldAccessor accessor, object value
) | 280 private static bool IsDefaultValue(IFieldAccessor accessor, object value
) |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
370 return (long) value == 0; | 316 return (long) value == 0; |
371 case FieldType.Float: | 317 case FieldType.Float: |
372 return (float) value == 0f; | 318 return (float) value == 0f; |
373 case FieldType.Message: | 319 case FieldType.Message: |
374 case FieldType.Group: // Never expect to get this, but... | 320 case FieldType.Group: // Never expect to get this, but... |
375 return value == null; | 321 return value == null; |
376 default: | 322 default: |
377 throw new ArgumentException("Invalid field type"); | 323 throw new ArgumentException("Invalid field type"); |
378 } | 324 } |
379 } | 325 } |
380 | 326 |
381 private void WriteValue(TextWriter writer, object value) | 327 /// <summary> |
| 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) |
382 { | 336 { |
383 if (value == null) | 337 if (value == null) |
384 { | 338 { |
385 WriteNull(writer); | 339 WriteNull(writer); |
386 } | 340 } |
387 else if (value is bool) | 341 else if (value is bool) |
388 { | 342 { |
389 writer.Write((bool)value ? "true" : "false"); | 343 writer.Write((bool)value ? "true" : "false"); |
390 } | 344 } |
391 else if (value is ByteString) | 345 else if (value is ByteString) |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 writer.Write(text); | 394 writer.Write(text); |
441 writer.Write('"'); | 395 writer.Write('"'); |
442 } | 396 } |
443 else | 397 else |
444 { | 398 { |
445 writer.Write(text); | 399 writer.Write(text); |
446 } | 400 } |
447 } | 401 } |
448 else if (value is IMessage) | 402 else if (value is IMessage) |
449 { | 403 { |
450 IMessage message = (IMessage) value; | 404 Format((IMessage)value, writer); |
451 if (message.Descriptor.IsWellKnownType) | |
452 { | |
453 WriteWellKnownTypeValue(writer, message.Descriptor, value); | |
454 } | |
455 else | |
456 { | |
457 WriteMessage(writer, (IMessage)value); | |
458 } | |
459 } | 405 } |
460 else | 406 else |
461 { | 407 { |
462 throw new ArgumentException("Unable to format value of type " +
value.GetType()); | 408 throw new ArgumentException("Unable to format value of type " +
value.GetType()); |
463 } | 409 } |
464 } | 410 } |
465 | 411 |
466 /// <summary> | 412 /// <summary> |
467 /// Central interception point for well-known type formatting. Any well-
known types which | 413 /// Central interception point for well-known type formatting. Any well-
known types which |
468 /// don't need special handling can fall back to WriteMessage. We avoid
assuming that the | 414 /// 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... |
717 } | 663 } |
718 WriteString(writer, keyText); | 664 WriteString(writer, keyText); |
719 writer.Write(NameValueSeparator); | 665 writer.Write(NameValueSeparator); |
720 WriteValue(writer, pair.Value); | 666 WriteValue(writer, pair.Value); |
721 first = false; | 667 first = false; |
722 } | 668 } |
723 writer.Write(first ? "}" : " }"); | 669 writer.Write(first ? "}" : " }"); |
724 } | 670 } |
725 | 671 |
726 /// <summary> | 672 /// <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> | |
741 /// Writes a string (including leading and trailing double quotes) to a
builder, escaping as required. | 673 /// Writes a string (including leading and trailing double quotes) to a
builder, escaping as required. |
742 /// </summary> | 674 /// </summary> |
743 /// <remarks> | 675 /// <remarks> |
744 /// Other than surrogate pair handling, this code is mostly taken from s
rc/google/protobuf/util/internal/json_escaping.cc. | 676 /// Other than surrogate pair handling, this code is mostly taken from s
rc/google/protobuf/util/internal/json_escaping.cc. |
745 /// </remarks> | 677 /// </remarks> |
746 internal static void WriteString(TextWriter writer, string text) | 678 internal static void WriteString(TextWriter writer, string text) |
747 { | 679 { |
748 writer.Write('"'); | 680 writer.Write('"'); |
749 for (int i = 0; i < text.Length; i++) | 681 for (int i = 0; i < text.Length; i++) |
750 { | 682 { |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
892 dictionaries[enumType] = nameMapping; | 824 dictionaries[enumType] = nameMapping; |
893 } | 825 } |
894 } | 826 } |
895 | 827 |
896 string originalName; | 828 string originalName; |
897 // If this returns false, originalName will be null, which is wh
at we want. | 829 // If this returns false, originalName will be null, which is wh
at we want. |
898 nameMapping.TryGetValue(value, out originalName); | 830 nameMapping.TryGetValue(value, out originalName); |
899 return originalName; | 831 return originalName; |
900 } | 832 } |
901 | 833 |
| 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 |
902 private static Dictionary<object, string> GetNameMapping(System.Type
enumType) => | 844 private static Dictionary<object, string> GetNameMapping(System.Type
enumType) => |
903 enumType.GetTypeInfo().DeclaredFields | 845 enumType.GetTypeInfo().DeclaredFields |
904 .Where(f => f.IsStatic) | 846 .Where(f => f.IsStatic) |
905 .ToDictionary(f => f.GetValue(null), | 847 .ToDictionary(f => f.GetValue(null), |
906 f => f.GetCustomAttributes<OriginalNameAttribu
te>() | 848 f => f.GetCustomAttributes<OriginalNameAttribu
te>() |
907 .FirstOrDefault() | 849 .FirstOrDefault() |
908 // If the attribute hasn't been applied,
fall back to the name of the field. | 850 // If the attribute hasn't been applied,
fall back to the name of the field. |
909 ?.Name ?? f.Name); | 851 ?.Name ?? f.Name); |
| 852 #endif |
910 } | 853 } |
911 } | 854 } |
912 } | 855 } |
OLD | NEW |