Commit 62d7fe56 authored by Jon Skeet's avatar Jon Skeet Committed by Jon Skeet

Make Any easier to work with in C#

- Add a TryUnpack method which doesn't throw if the type is wrong
- Make GetTypeName public for easier determination of the message type

Fixes #3294.
parent ecca6ea9
......@@ -90,6 +90,24 @@ namespace Google.Protobuf.WellKnownTypes
Assert.AreEqual(message, unpacked);
}
[Test]
public void TryUnpack_WrongType()
{
var message = SampleMessages.CreateFullTestAllTypes();
var any = Any.Pack(message);
Assert.False(any.TryUnpack(out TestOneof unpacked));
Assert.Null(unpacked);
}
[Test]
public void TryUnpack_RightType()
{
var message = SampleMessages.CreateFullTestAllTypes();
var any = Any.Pack(message);
Assert.IsTrue(any.TryUnpack(out TestAllTypes unpacked));
Assert.AreEqual(message, unpacked);
}
[Test]
public void ToString_WithValues()
{
......@@ -99,6 +117,16 @@ namespace Google.Protobuf.WellKnownTypes
Assert.That(text, Does.Contain("\"@value\": \"" + message.ToByteString().ToBase64() + "\""));
}
[Test]
[TestCase("proto://foo.bar", "foo.bar")]
[TestCase("/foo/bar/baz", "baz")]
[TestCase("foobar", "")]
public void GetTypeName(string typeUrl, string expectedTypeName)
{
var any = new Any { TypeUrl = typeUrl };
Assert.AreEqual(expectedTypeName, Any.GetTypeName(typeUrl));
}
[Test]
public void ToString_Empty()
{
......
......@@ -44,17 +44,25 @@ namespace Google.Protobuf.WellKnownTypes
prefix.EndsWith("/") ? prefix + descriptor.FullName : prefix + "/" + descriptor.FullName;
/// <summary>
/// Retrieves the type name for a type URL. This is always just the last part of the URL,
/// after the trailing slash. No validation of anything before the trailing slash is performed.
/// If the type URL does not include a slash, an empty string is returned rather than an exception
/// being thrown; this won't match any types, and the calling code is probably in a better position
/// to give a meaningful error.
/// There is no handling of fragments or queries at the moment.
/// Retrieves the type name for a type URL, matching the <see cref="DescriptorBase.FullName"/>
/// of the packed message type.
/// </summary>
/// <remarks>
/// <para>
/// This is always just the last part of the URL, after the final slash. No validation of
/// anything before the trailing slash is performed. If the type URL does not include a slash,
/// an empty string is returned rather than an exception being thrown; this won't match any types,
/// and the calling code is probably in a better position to give a meaningful error.
/// </para>
/// <para>
/// There is no handling of fragments or queries at the moment.
/// </para>
/// </remarks>
/// <param name="typeUrl">The URL to extract the type name from</param>
/// <returns>The type name</returns>
internal static string GetTypeName(string typeUrl)
public static string GetTypeName(string typeUrl)
{
ProtoPreconditions.CheckNotNull(typeUrl, nameof(typeUrl));
int lastSlash = typeUrl.LastIndexOf('/');
return lastSlash == -1 ? "" : typeUrl.Substring(lastSlash + 1);
}
......@@ -80,6 +88,27 @@ namespace Google.Protobuf.WellKnownTypes
return target;
}
/// <summary>
/// Attempts to unpack the content of this Any message into the target message type,
/// if it matches the type URL within this Any message.
/// </summary>
/// <typeparam name="T">The type of message to attempt to unpack the content into.</typeparam>
/// <returns><c>true</c> if the message was successfully unpacked; <c>false</c> if the type name didn't match</returns>
public bool TryUnpack<T>(out T result) where T : IMessage, new()
{
// Note: deliberately avoid writing anything to result until the end, in case it's being
// monitored by other threads. (That would be a bug in the calling code, but let's not make it worse.)
T target = new T();
if (GetTypeName(TypeUrl) != target.Descriptor.FullName)
{
result = default(T); // Can't use null as there's no class constraint, but this always *will* be null in real usage.
return false;
}
target.MergeFrom(Value);
result = target;
return true;
}
/// <summary>
/// Packs the specified message into an Any message using a type URL prefix of "type.googleapis.com".
/// </summary>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment