diff options
author | Jon Skeet <jonskeet@google.com> | 2015-11-05 19:44:26 +0000 |
---|---|---|
committer | Jon Skeet <jonskeet@google.com> | 2015-11-05 19:44:26 +0000 |
commit | 6fa17e759737e3225c6cc4ba830b921428c50781 (patch) | |
tree | ea934e5609107db7ce98dda39aa664839d3afb94 /csharp/src/Google.Protobuf/JsonParser.cs | |
parent | 3d257a9dc1427acf163603cea95fb015e839bd2b (diff) | |
download | protobuf-6fa17e759737e3225c6cc4ba830b921428c50781.tar.gz protobuf-6fa17e759737e3225c6cc4ba830b921428c50781.tar.bz2 protobuf-6fa17e759737e3225c6cc4ba830b921428c50781.zip |
Reimplement JSON recursion by detecting the depth in the tokenizer.
Added a TODO around a possible change to the tokenizer API, changing PushBack(token) into just Rewind() or something similar.
Diffstat (limited to 'csharp/src/Google.Protobuf/JsonParser.cs')
-rw-r--r-- | csharp/src/Google.Protobuf/JsonParser.cs | 67 |
1 files changed, 33 insertions, 34 deletions
diff --git a/csharp/src/Google.Protobuf/JsonParser.cs b/csharp/src/Google.Protobuf/JsonParser.cs index 2ab8c110..4cbbd89d 100644 --- a/csharp/src/Google.Protobuf/JsonParser.cs +++ b/csharp/src/Google.Protobuf/JsonParser.cs @@ -69,16 +69,16 @@ namespace Google.Protobuf // TODO: Consider introducing a class containing parse state of the parser, tokenizer and depth. That would simplify these handlers // and the signatures of various methods. - private static readonly Dictionary<string, Action<JsonParser, IMessage, JsonTokenizer, int>> - WellKnownTypeHandlers = new Dictionary<string, Action<JsonParser, IMessage, JsonTokenizer, int>> + private static readonly Dictionary<string, Action<JsonParser, IMessage, JsonTokenizer>> + WellKnownTypeHandlers = new Dictionary<string, Action<JsonParser, IMessage, JsonTokenizer>> { - { Timestamp.Descriptor.FullName, (parser, message, tokenizer, depth) => MergeTimestamp(message, tokenizer.Next()) }, - { Duration.Descriptor.FullName, (parser, message, tokenizer, depth) => MergeDuration(message, tokenizer.Next()) }, - { Value.Descriptor.FullName, (parser, message, tokenizer, depth) => parser.MergeStructValue(message, tokenizer, depth) }, - { ListValue.Descriptor.FullName, (parser, message, tokenizer, depth) => - parser.MergeRepeatedField(message, message.Descriptor.Fields[ListValue.ValuesFieldNumber], tokenizer, depth) }, - { Struct.Descriptor.FullName, (parser, message, tokenizer, depth) => parser.MergeStruct(message, tokenizer, depth) }, - { FieldMask.Descriptor.FullName, (parser, message, tokenizer, depth) => MergeFieldMask(message, tokenizer.Next()) }, + { Timestamp.Descriptor.FullName, (parser, message, tokenizer) => MergeTimestamp(message, tokenizer.Next()) }, + { Duration.Descriptor.FullName, (parser, message, tokenizer) => MergeDuration(message, tokenizer.Next()) }, + { Value.Descriptor.FullName, (parser, message, tokenizer) => parser.MergeStructValue(message, tokenizer) }, + { ListValue.Descriptor.FullName, (parser, message, tokenizer) => + parser.MergeRepeatedField(message, message.Descriptor.Fields[ListValue.ValuesFieldNumber], tokenizer) }, + { Struct.Descriptor.FullName, (parser, message, tokenizer) => parser.MergeStruct(message, tokenizer) }, + { FieldMask.Descriptor.FullName, (parser, message, tokenizer) => MergeFieldMask(message, tokenizer.Next()) }, { Int32Value.Descriptor.FullName, MergeWrapperField }, { Int64Value.Descriptor.FullName, MergeWrapperField }, { UInt32Value.Descriptor.FullName, MergeWrapperField }, @@ -91,9 +91,9 @@ namespace Google.Protobuf // Convenience method to avoid having to repeat the same code multiple times in the above // dictionary initialization. - private static void MergeWrapperField(JsonParser parser, IMessage message, JsonTokenizer tokenizer, int depth) + private static void MergeWrapperField(JsonParser parser, IMessage message, JsonTokenizer tokenizer) { - parser.MergeField(message, message.Descriptor.Fields[Wrappers.WrapperValueFieldNumber], tokenizer, depth); + parser.MergeField(message, message.Descriptor.Fields[Wrappers.WrapperValueFieldNumber], tokenizer); } /// <summary> @@ -130,7 +130,7 @@ namespace Google.Protobuf internal void Merge(IMessage message, TextReader jsonReader) { var tokenizer = new JsonTokenizer(jsonReader); - Merge(message, tokenizer, 0); + Merge(message, tokenizer); var lastToken = tokenizer.Next(); if (lastToken != JsonToken.EndDocument) { @@ -145,19 +145,18 @@ namespace Google.Protobuf /// of tokens provided by the tokenizer. This token stream is assumed to be valid JSON, with the /// tokenizer performing that validation - but not every token stream is valid "protobuf JSON". /// </summary> - private void Merge(IMessage message, JsonTokenizer tokenizer, int depth) + private void Merge(IMessage message, JsonTokenizer tokenizer) { - if (depth > settings.RecursionLimit) + if (tokenizer.ObjectDepth > settings.RecursionLimit) { - throw InvalidProtocolBufferException.RecursionLimitExceeded(); + throw InvalidProtocolBufferException.JsonRecursionLimitExceeded(); } - depth++; if (message.Descriptor.IsWellKnownType) { - Action<JsonParser, IMessage, JsonTokenizer, int> handler; + Action<JsonParser, IMessage, JsonTokenizer> handler; if (WellKnownTypeHandlers.TryGetValue(message.Descriptor.FullName, out handler)) { - handler(this, message, tokenizer, depth); + handler(this, message, tokenizer); return; } // Well-known types with no special handling continue in the normal way. @@ -188,7 +187,7 @@ namespace Google.Protobuf FieldDescriptor field; if (jsonFieldMap.TryGetValue(name, out field)) { - MergeField(message, field, tokenizer, depth); + MergeField(message, field, tokenizer); } else { @@ -200,7 +199,7 @@ namespace Google.Protobuf } } - private void MergeField(IMessage message, FieldDescriptor field, JsonTokenizer tokenizer, int depth) + private void MergeField(IMessage message, FieldDescriptor field, JsonTokenizer tokenizer) { var token = tokenizer.Next(); if (token.Type == JsonToken.TokenType.Null) @@ -214,20 +213,20 @@ namespace Google.Protobuf if (field.IsMap) { - MergeMapField(message, field, tokenizer, depth); + MergeMapField(message, field, tokenizer); } else if (field.IsRepeated) { - MergeRepeatedField(message, field, tokenizer, depth); + MergeRepeatedField(message, field, tokenizer); } else { - var value = ParseSingleValue(field, tokenizer, depth); + var value = ParseSingleValue(field, tokenizer); field.Accessor.SetValue(message, value); } } - private void MergeRepeatedField(IMessage message, FieldDescriptor field, JsonTokenizer tokenizer, int depth) + private void MergeRepeatedField(IMessage message, FieldDescriptor field, JsonTokenizer tokenizer) { var token = tokenizer.Next(); if (token.Type != JsonToken.TokenType.StartArray) @@ -244,11 +243,11 @@ namespace Google.Protobuf return; } tokenizer.PushBack(token); - list.Add(ParseSingleValue(field, tokenizer, depth)); + list.Add(ParseSingleValue(field, tokenizer)); } } - private void MergeMapField(IMessage message, FieldDescriptor field, JsonTokenizer tokenizer, int depth) + private void MergeMapField(IMessage message, FieldDescriptor field, JsonTokenizer tokenizer) { // Map fields are always objects, even if the values are well-known types: ParseSingleValue handles those. var token = tokenizer.Next(); @@ -274,13 +273,13 @@ namespace Google.Protobuf return; } object key = ParseMapKey(keyField, token.StringValue); - object value = ParseSingleValue(valueField, tokenizer, depth); + object value = ParseSingleValue(valueField, tokenizer); // TODO: Null handling dictionary[key] = value; } } - private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tokenizer, int depth) + private object ParseSingleValue(FieldDescriptor field, JsonTokenizer tokenizer) { var token = tokenizer.Next(); if (token.Type == JsonToken.TokenType.Null) @@ -308,7 +307,7 @@ namespace Google.Protobuf // TODO: Merge the current value in message? (Public API currently doesn't make this relevant as we don't expose merging.) tokenizer.PushBack(token); IMessage subMessage = NewMessageForField(field); - Merge(subMessage, tokenizer, depth); + Merge(subMessage, tokenizer); return subMessage; } } @@ -358,7 +357,7 @@ namespace Google.Protobuf return message; } - private void MergeStructValue(IMessage message, JsonTokenizer tokenizer, int depth) + private void MergeStructValue(IMessage message, JsonTokenizer tokenizer) { var firstToken = tokenizer.Next(); var fields = message.Descriptor.Fields; @@ -382,7 +381,7 @@ namespace Google.Protobuf var field = fields[Value.StructValueFieldNumber]; var structMessage = NewMessageForField(field); tokenizer.PushBack(firstToken); - Merge(structMessage, tokenizer, depth); + Merge(structMessage, tokenizer); field.Accessor.SetValue(message, structMessage); return; } @@ -391,7 +390,7 @@ namespace Google.Protobuf var field = fields[Value.ListValueFieldNumber]; var list = NewMessageForField(field); tokenizer.PushBack(firstToken); - Merge(list, tokenizer, depth); + Merge(list, tokenizer); field.Accessor.SetValue(message, list); return; } @@ -400,7 +399,7 @@ namespace Google.Protobuf } } - private void MergeStruct(IMessage message, JsonTokenizer tokenizer, int depth) + private void MergeStruct(IMessage message, JsonTokenizer tokenizer) { var token = tokenizer.Next(); if (token.Type != JsonToken.TokenType.StartObject) @@ -410,7 +409,7 @@ namespace Google.Protobuf tokenizer.PushBack(token); var field = message.Descriptor.Fields[Struct.FieldsFieldNumber]; - MergeMapField(message, field, tokenizer, depth); + MergeMapField(message, field, tokenizer); } #region Utility methods which don't depend on the state (or settings) of the parser. |