public void ToCamelCase(string original, string expected) { Assert.AreEqual(expected, JsonFormatter.ToCamelCase(original)); }
/// <summary> /// Merges the given message using data from the given tokenizer. In most cases, the next /// token should be a "start object" token, but wrapper types and nullity can invalidate /// that assumption. This is implemented as an LL(1) recursive descent parser over the stream /// 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) { if (message.Descriptor.IsWellKnownType) { Action <JsonParser, IMessage, JsonTokenizer> handler; if (WellKnownTypeHandlers.TryGetValue(message.Descriptor.FullName, out handler)) { handler(this, message, tokenizer); return; } // Well-known types with no special handling continue in the normal way. } var token = tokenizer.Next(); if (token.Type != JsonToken.TokenType.StartObject) { throw new InvalidProtocolBufferException("Expected an object"); } var descriptor = message.Descriptor; // TODO: Make this more efficient, e.g. by building it once in the descriptor. // Additionally, we need to consider whether to parse field names in their original proto form, // and any overrides in the descriptor. But yes, all of this should be in the descriptor somehow... // the descriptor can expose the dictionary. var jsonFieldMap = descriptor.Fields.InDeclarationOrder().ToDictionary(field => JsonFormatter.ToCamelCase(field.Name)); while (true) { token = tokenizer.Next(); if (token.Type == JsonToken.TokenType.EndObject) { return; } if (token.Type != JsonToken.TokenType.Name) { throw new InvalidOperationException("Unexpected token type " + token.Type); } string name = token.StringValue; FieldDescriptor field; if (jsonFieldMap.TryGetValue(name, out field)) { MergeField(message, field, tokenizer); } else { // TODO: Is this what we want to do? If not, we'll need to skip the value, // which may be an object or array. (We might want to put code in the tokenizer // to do that.) throw new InvalidProtocolBufferException("Unknown field: " + name); } } }