private void MergeAny(IMessage message, JsonTokenizer tokenizer) { // Record the token stream until we see the @type property. At that point, we can take the value, consult // the type registry for the relevant message, and replay the stream, omitting the @type property. var tokens = new List <JsonToken>(); var token = tokenizer.Next(); if (token.Type != JsonToken.TokenType.StartObject) { throw new InvalidProtocolBufferException("Expected object value for Any"); } int typeUrlObjectDepth = tokenizer.ObjectDepth; // The check for the property depth protects us from nested Any values which occur before the type URL // for *this* Any. while (token.Type != JsonToken.TokenType.Name || token.StringValue != JsonFormatter.AnyTypeUrlField || tokenizer.ObjectDepth != typeUrlObjectDepth) { tokens.Add(token); token = tokenizer.Next(); if (tokenizer.ObjectDepth < typeUrlObjectDepth) { throw new InvalidProtocolBufferException("Any message with no @type"); } } // Don't add the @type property or its value to the recorded token list token = tokenizer.Next(); if (token.Type != JsonToken.TokenType.StringValue) { throw new InvalidProtocolBufferException("Expected string value for Any.@type"); } string typeUrl = token.StringValue; string typeName = Any.GetTypeName(typeUrl); MessageDescriptor descriptor = settings.TypeRegistry.Find(typeName); if (descriptor == null) { throw new InvalidOperationException($"Type registry has no descriptor for type name '{typeName}'"); } // Now replay the token stream we've already read and anything that remains of the object, just parsing it // as normal. Our original tokenizer should end up at the end of the object. var replay = JsonTokenizer.FromReplayedTokens(tokens, tokenizer); var body = descriptor.Parser.CreateTemplate(); if (descriptor.IsWellKnownType) { MergeWellKnownTypeAnyBody(body, replay); } else { Merge(body, replay); } var data = body.ToByteString(); // Now that we have the message data, we can pack it into an Any (the message received as a parameter). message.Descriptor.Fields[Any.TypeUrlFieldNumber].Accessor.SetValue(message, typeUrl); message.Descriptor.Fields[Any.ValueFieldNumber].Accessor.SetValue(message, data); }