Commit 9440a2ab authored by Jon Skeet's avatar Jon Skeet

Merge pull request #582 from jskeet/csharp-json

JSON formatting in C#
parents b918dc1b 0f34daad
...@@ -40,13 +40,13 @@ namespace Google.Protobuf.Examples.AddressBook { ...@@ -40,13 +40,13 @@ namespace Google.Protobuf.Examples.AddressBook {
}); });
internal__static_tutorial_Person__FieldAccessorTable = internal__static_tutorial_Person__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person), descriptor.MessageTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person), descriptor.MessageTypes[0],
new string[] { "Name", "Id", "Email", "Phone", }); new string[] { "Name", "Id", "Email", "Phone", }, new string[] { });
internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable = internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), descriptor.MessageTypes[0].NestedTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.Person.Types.PhoneNumber), descriptor.MessageTypes[0].NestedTypes[0],
new string[] { "Number", "Type", }); new string[] { "Number", "Type", }, new string[] { });
internal__static_tutorial_AddressBook__FieldAccessorTable = internal__static_tutorial_AddressBook__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), descriptor.MessageTypes[1], new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.Examples.AddressBook.AddressBook), descriptor.MessageTypes[1],
new string[] { "Person", }); new string[] { "Person", }, new string[] { });
} }
#endregion #endregion
...@@ -160,6 +160,10 @@ namespace Google.Protobuf.Examples.AddressBook { ...@@ -160,6 +160,10 @@ namespace Google.Protobuf.Examples.AddressBook {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (Name.Length != 0) { if (Name.Length != 0) {
output.WriteRawTag(10); output.WriteRawTag(10);
...@@ -330,6 +334,10 @@ namespace Google.Protobuf.Examples.AddressBook { ...@@ -330,6 +334,10 @@ namespace Google.Protobuf.Examples.AddressBook {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (Number.Length != 0) { if (Number.Length != 0) {
output.WriteRawTag(10); output.WriteRawTag(10);
...@@ -463,6 +471,10 @@ namespace Google.Protobuf.Examples.AddressBook { ...@@ -463,6 +471,10 @@ namespace Google.Protobuf.Examples.AddressBook {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
person_.WriteTo(output, _repeated_person_codec); person_.WriteTo(output, _repeated_person_codec);
} }
......
...@@ -734,5 +734,25 @@ namespace Google.Protobuf ...@@ -734,5 +734,25 @@ namespace Google.Protobuf
var message = SampleMessages.CreateFullTestAllTypes(); var message = SampleMessages.CreateFullTestAllTypes();
Assert.Throws<InvalidCastException>(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap())); Assert.Throws<InvalidCastException>(() => message.Fields[TestAllTypes.SingleBoolFieldNumber].GetValue(new TestMap()));
} }
[Test]
public void Reflection_Oneof()
{
var message = new TestAllTypes();
var fields = message.Fields;
Assert.AreEqual(1, fields.Oneofs.Count);
var oneof = fields.Oneofs[0];
Assert.AreEqual("oneof_field", oneof.Descriptor.Name);
Assert.IsNull(oneof.GetCaseFieldDescriptor(message));
message.OneofString = "foo";
Assert.AreSame(fields[TestAllTypes.OneofStringFieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message));
message.OneofUint32 = 10;
Assert.AreSame(fields[TestAllTypes.OneofUint32FieldNumber].Descriptor, oneof.GetCaseFieldDescriptor(message));
oneof.Clear(message);
Assert.AreEqual(TestAllTypes.OneofFieldOneofCase.None, message.OneofFieldCase);
}
} }
} }
This diff is collapsed.
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
<Compile Include="GeneratedMessageTest.cs" /> <Compile Include="GeneratedMessageTest.cs" />
<Compile Include="Collections\MapFieldTest.cs" /> <Compile Include="Collections\MapFieldTest.cs" />
<Compile Include="Collections\RepeatedFieldTest.cs" /> <Compile Include="Collections\RepeatedFieldTest.cs" />
<Compile Include="JsonFormatterTest.cs" />
<Compile Include="SampleEnum.cs" /> <Compile Include="SampleEnum.cs" />
<Compile Include="SampleMessages.cs" /> <Compile Include="SampleMessages.cs" />
<Compile Include="TestProtos\MapUnittestProto3.cs" /> <Compile Include="TestProtos\MapUnittestProto3.cs" />
......
...@@ -38,7 +38,7 @@ namespace Google.Protobuf.TestProtos { ...@@ -38,7 +38,7 @@ namespace Google.Protobuf.TestProtos {
}); });
internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable = internal__static_protobuf_unittest_import_ImportMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.ImportMessage), descriptor.MessageTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.ImportMessage), descriptor.MessageTypes[0],
new string[] { "D", }); new string[] { "D", }, new string[] { });
} }
#endregion #endregion
...@@ -124,6 +124,10 @@ namespace Google.Protobuf.TestProtos { ...@@ -124,6 +124,10 @@ namespace Google.Protobuf.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (D != 0) { if (D != 0) {
output.WriteRawTag(8); output.WriteRawTag(8);
......
...@@ -33,7 +33,7 @@ namespace Google.Protobuf.TestProtos { ...@@ -33,7 +33,7 @@ namespace Google.Protobuf.TestProtos {
}); });
internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable = internal__static_protobuf_unittest_import_PublicImportMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), descriptor.MessageTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::Google.Protobuf.TestProtos.PublicImportMessage), descriptor.MessageTypes[0],
new string[] { "E", }); new string[] { "E", }, new string[] { });
} }
#endregion #endregion
...@@ -109,6 +109,10 @@ namespace Google.Protobuf.TestProtos { ...@@ -109,6 +109,10 @@ namespace Google.Protobuf.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (E != 0) { if (E != 0) {
output.WriteRawTag(8); output.WriteRawTag(8);
......
...@@ -53,25 +53,25 @@ namespace UnitTest.Issues.TestProtos { ...@@ -53,25 +53,25 @@ namespace UnitTest.Issues.TestProtos {
}); });
internal__static_unittest_issues_Issue307__FieldAccessorTable = internal__static_unittest_issues_Issue307__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307), descriptor.MessageTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307), descriptor.MessageTypes[0],
new string[] { }); new string[] { }, new string[] { });
internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable = internal__static_unittest_issues_Issue307_NestedOnce__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), descriptor.MessageTypes[0].NestedTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce), descriptor.MessageTypes[0].NestedTypes[0],
new string[] { }); new string[] { }, new string[] { });
internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable = internal__static_unittest_issues_Issue307_NestedOnce_NestedTwice__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), descriptor.MessageTypes[0].NestedTypes[0].NestedTypes[0], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.Issue307.Types.NestedOnce.Types.NestedTwice), descriptor.MessageTypes[0].NestedTypes[0].NestedTypes[0],
new string[] { }); new string[] { }, new string[] { });
internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable = internal__static_unittest_issues_NegativeEnumMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), descriptor.MessageTypes[1], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.NegativeEnumMessage), descriptor.MessageTypes[1],
new string[] { "Value", "Values", "PackedValues", }); new string[] { "Value", "Values", "PackedValues", }, new string[] { });
internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable = internal__static_unittest_issues_DeprecatedChild__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), descriptor.MessageTypes[2], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedChild), descriptor.MessageTypes[2],
new string[] { }); new string[] { }, new string[] { });
internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable = internal__static_unittest_issues_DeprecatedFieldsMessage__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), descriptor.MessageTypes[3], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.DeprecatedFieldsMessage), descriptor.MessageTypes[3],
new string[] { "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray", }); new string[] { "PrimitiveValue", "PrimitiveArray", "MessageValue", "MessageArray", "EnumValue", "EnumArray", }, new string[] { });
internal__static_unittest_issues_ItemField__FieldAccessorTable = internal__static_unittest_issues_ItemField__FieldAccessorTable =
new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.ItemField), descriptor.MessageTypes[4], new pb::FieldAccess.FieldAccessorTable(typeof(global::UnitTest.Issues.TestProtos.ItemField), descriptor.MessageTypes[4],
new string[] { "Item", }); new string[] { "Item", }, new string[] { });
} }
#endregion #endregion
...@@ -148,6 +148,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -148,6 +148,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
} }
...@@ -237,6 +241,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -237,6 +241,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
} }
...@@ -326,6 +334,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -326,6 +334,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
} }
...@@ -459,6 +471,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -459,6 +471,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) { if (Value != global::UnitTest.Issues.TestProtos.NegativeEnum.NEGATIVE_ENUM_ZERO) {
output.WriteRawTag(8); output.WriteRawTag(8);
...@@ -577,6 +593,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -577,6 +593,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
} }
...@@ -746,6 +766,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -746,6 +766,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (PrimitiveValue != 0) { if (PrimitiveValue != 0) {
output.WriteRawTag(8); output.WriteRawTag(8);
...@@ -918,6 +942,10 @@ namespace UnitTest.Issues.TestProtos { ...@@ -918,6 +942,10 @@ namespace UnitTest.Issues.TestProtos {
return hash; return hash;
} }
public override string ToString() {
return pb::JsonFormatter.Default.Format(this);
}
public void WriteTo(pb::CodedOutputStream output) { public void WriteTo(pb::CodedOutputStream output) {
if (Item != 0) { if (Item != 0) {
output.WriteRawTag(8); output.WriteRawTag(8);
......
...@@ -89,6 +89,7 @@ namespace Google.Protobuf.Descriptors ...@@ -89,6 +89,7 @@ namespace Google.Protobuf.Descriptors
/// <summary> /// <summary>
/// Finds an enum value by number. If multiple enum values have the /// Finds an enum value by number. If multiple enum values have the
/// same number, this returns the first defined value with that number. /// same number, this returns the first defined value with that number.
/// If there is no value for the given number, this returns <c>null</c>.
/// </summary> /// </summary>
public EnumValueDescriptor FindValueByNumber(int number) public EnumValueDescriptor FindValueByNumber(int number)
{ {
......
...@@ -42,6 +42,7 @@ namespace Google.Protobuf.FieldAccess ...@@ -42,6 +42,7 @@ namespace Google.Protobuf.FieldAccess
public sealed class FieldAccessorTable public sealed class FieldAccessorTable
{ {
private readonly ReadOnlyCollection<IFieldAccessor> accessors; private readonly ReadOnlyCollection<IFieldAccessor> accessors;
private readonly ReadOnlyCollection<OneofAccessor> oneofs;
private readonly MessageDescriptor descriptor; private readonly MessageDescriptor descriptor;
/// <summary> /// <summary>
...@@ -51,7 +52,7 @@ namespace Google.Protobuf.FieldAccess ...@@ -51,7 +52,7 @@ namespace Google.Protobuf.FieldAccess
/// <param name="type">The CLR type for the message.</param> /// <param name="type">The CLR type for the message.</param>
/// <param name="descriptor">The type's descriptor</param> /// <param name="descriptor">The type's descriptor</param>
/// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param> /// <param name="propertyNames">The Pascal-case names of all the field-based properties in the message.</param>
public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames) public FieldAccessorTable(Type type, MessageDescriptor descriptor, string[] propertyNames, string[] oneofPropertyNames)
{ {
this.descriptor = descriptor; this.descriptor = descriptor;
var accessorsArray = new IFieldAccessor[descriptor.Fields.Count]; var accessorsArray = new IFieldAccessor[descriptor.Fields.Count];
...@@ -65,7 +66,13 @@ namespace Google.Protobuf.FieldAccess ...@@ -65,7 +66,13 @@ namespace Google.Protobuf.FieldAccess
: (IFieldAccessor) new SingleFieldAccessor(type, name, field); : (IFieldAccessor) new SingleFieldAccessor(type, name, field);
} }
accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray); accessors = new ReadOnlyCollection<IFieldAccessor>(accessorsArray);
// TODO(jonskeet): Oneof support var oneofsArray = new OneofAccessor[descriptor.Oneofs.Count];
for (int i = 0; i < oneofsArray.Length; i++)
{
var oneof = descriptor.Oneofs[i];
oneofsArray[i] = new OneofAccessor(type, oneofPropertyNames[i], oneof);
}
oneofs = new ReadOnlyCollection<OneofAccessor>(oneofsArray);
} }
// TODO: Validate the name here... should possibly make this type a more "general reflection access" type, // TODO: Validate the name here... should possibly make this type a more "general reflection access" type,
...@@ -75,6 +82,10 @@ namespace Google.Protobuf.FieldAccess ...@@ -75,6 +82,10 @@ namespace Google.Protobuf.FieldAccess
/// </summary> /// </summary>
public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } } public ReadOnlyCollection<IFieldAccessor> Accessors { get { return accessors; } }
public ReadOnlyCollection<OneofAccessor> Oneofs { get { return oneofs; } }
// TODO: Review this, as it's easy to get confused between FieldNumber and Index.
// Currently only used to get an accessor related to a oneof... maybe just make that simpler?
public IFieldAccessor this[int fieldNumber] public IFieldAccessor this[int fieldNumber]
{ {
get get
...@@ -83,17 +94,5 @@ namespace Google.Protobuf.FieldAccess ...@@ -83,17 +94,5 @@ namespace Google.Protobuf.FieldAccess
return accessors[field.Index]; return accessors[field.Index];
} }
} }
internal IFieldAccessor this[FieldDescriptor field]
{
get
{
if (field.ContainingType != descriptor)
{
throw new ArgumentException("FieldDescriptor does not match message type.");
}
return accessors[field.Index];
}
}
} }
} }
\ No newline at end of file
...@@ -44,6 +44,8 @@ namespace Google.Protobuf.FieldAccess ...@@ -44,6 +44,8 @@ namespace Google.Protobuf.FieldAccess
/// </summary> /// </summary>
FieldDescriptor Descriptor { get; } FieldDescriptor Descriptor { get; }
// TODO: Should the argument type for these messages by IReflectedMessage?
/// <summary> /// <summary>
/// Clears the field in the specified message. (For repeated fields, /// Clears the field in the specified message. (For repeated fields,
/// this clears the list.) /// this clears the list.)
......
...@@ -30,62 +30,57 @@ ...@@ -30,62 +30,57 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#endregion #endregion
using Google.Protobuf.Descriptors;
using System;
using System.Reflection;
namespace Google.Protobuf.FieldAccess namespace Google.Protobuf.FieldAccess
{ {
// TODO(jonskeet): Add "new" oneof API support
/// <summary> /// <summary>
/// Access for an oneof /// Reflection access for a oneof, allowing clear and "get case" actions.
/// </summary> /// </summary>
internal class OneofAccessor<TMessage> where TMessage : IMessage<TMessage> public sealed class OneofAccessor
{ {
/* private readonly Func<object, int> caseDelegate;
private readonly Func<TMessage, object> caseDelegate; private readonly Action<object> clearDelegate;
private readonly Func<TBuilder, IBuilder> clearDelegate; private OneofDescriptor descriptor;
private MessageDescriptor descriptor;
internal OneofAccessor(MessageDescriptor descriptor, string name) internal OneofAccessor(Type type, string propertyName, OneofDescriptor descriptor)
{ {
this.descriptor = descriptor; PropertyInfo property = type.GetProperty(propertyName + "Case");
MethodInfo clearMethod = typeof(TBuilder).GetMethod("Clear" + name); if (property == null || !property.CanRead)
PropertyInfo caseProperty = typeof(TMessage).GetProperty(name + "Case");
if (clearMethod == null || caseProperty == null)
{ {
throw new ArgumentException("Not all required properties/methods available for oneof"); throw new ArgumentException("Not all required properties/methods available");
} }
this.descriptor = descriptor;
caseDelegate = ReflectionUtil.CreateFuncObjectT<int>(property.GetGetMethod());
clearDelegate = ReflectionUtil.CreateDelegateFunc<TBuilder, IBuilder>(clearMethod); this.descriptor = descriptor;
caseDelegate = ReflectionUtil.CreateUpcastDelegate<TMessage>(caseProperty.GetGetMethod()); MethodInfo clearMethod = type.GetMethod("Clear" + propertyName);
clearDelegate = ReflectionUtil.CreateActionObject(clearMethod);
} }
/// <summary> public OneofDescriptor Descriptor { get { return descriptor; } }
/// Indicates whether the specified message has set any field in the oneof.
/// </summary>
public bool Has(TMessage message)
{
return ((int) caseDelegate(message) != 0);
}
/// <summary> /// <summary>
/// Clears the oneof in the specified builder. /// Clears the oneof in the specified message.
/// </summary> /// </summary>
public void Clear(TBuilder builder) public void Clear(object message)
{ {
clearDelegate(builder); clearDelegate(message);
} }
/// <summary> /// <summary>
/// Indicates which field in the oneof is set for specified message /// Indicates which field in the oneof is set for specified message
/// </summary> /// </summary>
public virtual FieldDescriptor GetOneofFieldDescriptor(TMessage message) public FieldDescriptor GetCaseFieldDescriptor(object message)
{ {
int fieldNumber = (int) caseDelegate(message); int fieldNumber = caseDelegate(message);
if (fieldNumber > 0) if (fieldNumber > 0)
{ {
return descriptor.FindFieldByNumber(fieldNumber); return descriptor.ContainingType.FindFieldByNumber(fieldNumber);
} }
return null; return null;
}*/ }
} }
} }
...@@ -63,7 +63,20 @@ namespace Google.Protobuf.FieldAccess ...@@ -63,7 +63,20 @@ namespace Google.Protobuf.FieldAccess
Expression upcast = Expression.Convert(call, typeof(object)); Expression upcast = Expression.Convert(call, typeof(object));
return Expression.Lambda<Func<object, object>>(upcast, parameter).Compile(); return Expression.Lambda<Func<object, object>>(upcast, parameter).Compile();
} }
/// <summary>
/// Creates a delegate which will cast the argument to the appropriate method target type,
/// call the method on it, then convert the result to the specified type.
/// </summary>
internal static Func<object, T> CreateFuncObjectT<T>(MethodInfo method)
{
ParameterExpression parameter = Expression.Parameter(typeof(object), "p");
Expression downcast = Expression.Convert(parameter, method.DeclaringType);
Expression call = Expression.Call(downcast, method);
Expression upcast = Expression.Convert(call, typeof(T));
return Expression.Lambda<Func<object, T>>(upcast, parameter).Compile();
}
/// <summary> /// <summary>
/// Creates a delegate which will execute the given method after casting the first argument to /// Creates a delegate which will execute the given method after casting the first argument to
/// the target type of the method, and the second argument to the first parameter type of the method. /// the target type of the method, and the second argument to the first parameter type of the method.
......
...@@ -40,9 +40,9 @@ namespace Google.Protobuf ...@@ -40,9 +40,9 @@ namespace Google.Protobuf
// TODO(jonskeet): Split these interfaces into separate files when we're happy with them. // TODO(jonskeet): Split these interfaces into separate files when we're happy with them.
/// <summary> /// <summary>
/// Reflection support for a specific message type. /// Reflection support for accessing field values.
/// </summary> /// </summary>
public interface IReflectedMessage public interface IReflectedMessage : IMessage
{ {
FieldAccessorTable Fields { get; } FieldAccessorTable Fields { get; }
// TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"? // TODO(jonskeet): Descriptor? Or a single property which has "all you need for reflection"?
...@@ -81,7 +81,7 @@ namespace Google.Protobuf ...@@ -81,7 +81,7 @@ namespace Google.Protobuf
/// the implementation class. /// the implementation class.
/// </summary> /// </summary>
/// <typeparam name="T">The message type.</typeparam> /// <typeparam name="T">The message type.</typeparam>
public interface IMessage<T> : IMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T> public interface IMessage<T> : IReflectedMessage, IEquatable<T>, IDeepCloneable<T>, IFreezable where T : IMessage<T>
{ {
/// <summary> /// <summary>
/// Merges the given message into this one. /// Merges the given message into this one.
......
This diff is collapsed.
...@@ -81,6 +81,7 @@ ...@@ -81,6 +81,7 @@
<Compile Include="FieldCodec.cs" /> <Compile Include="FieldCodec.cs" />
<Compile Include="FrameworkPortability.cs" /> <Compile Include="FrameworkPortability.cs" />
<Compile Include="Freezable.cs" /> <Compile Include="Freezable.cs" />
<Compile Include="JsonFormatter.cs" />
<Compile Include="MessageExtensions.cs" /> <Compile Include="MessageExtensions.cs" />
<Compile Include="FieldAccess\FieldAccessorBase.cs" /> <Compile Include="FieldAccess\FieldAccessorBase.cs" />
<Compile Include="FieldAccess\ReflectionUtil.cs" /> <Compile Include="FieldAccess\ReflectionUtil.cs" />
......
...@@ -58,6 +58,7 @@ class FieldGeneratorBase : public SourceGeneratorBase { ...@@ -58,6 +58,7 @@ class FieldGeneratorBase : public SourceGeneratorBase {
virtual void WriteHash(io::Printer* printer) = 0; virtual void WriteHash(io::Printer* printer) = 0;
virtual void WriteEquals(io::Printer* printer) = 0; virtual void WriteEquals(io::Printer* printer) = 0;
// Currently unused, as we use reflection to generate JSON
virtual void WriteToString(io::Printer* printer) = 0; virtual void WriteToString(io::Printer* printer) = 0;
protected: protected:
......
...@@ -117,12 +117,9 @@ void MapFieldGenerator::WriteEquals(io::Printer* printer) { ...@@ -117,12 +117,9 @@ void MapFieldGenerator::WriteEquals(io::Printer* printer) {
variables_, variables_,
"if (!$property_name$.Equals(other.$property_name$)) return false;\n"); "if (!$property_name$.Equals(other.$property_name$)) return false;\n");
} }
void MapFieldGenerator::WriteToString(io::Printer* printer) { void MapFieldGenerator::WriteToString(io::Printer* printer) {
/* // TODO: If we ever actually use ToString, we'll need to impleme this...
variables_["field_name"] = GetFieldName(descriptor_);
printer->Print(
variables_,
"PrintField(\"$field_name$\", has$property_name$, $name$_, writer);\n");*/
} }
void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) { void MapFieldGenerator::GenerateCloningCode(io::Printer* printer) {
......
...@@ -151,6 +151,7 @@ void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer) ...@@ -151,6 +151,7 @@ void MessageGenerator::GenerateStaticVariableInitializers(io::Printer* printer)
printer->Print("\"$property_name$\", ", printer->Print("\"$property_name$\", ",
"property_name", GetPropertyName(descriptor_->field(i))); "property_name", GetPropertyName(descriptor_->field(i)));
} }
printer->Print("}, new string[] { ");
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
printer->Print("\"$oneof_name$\", ", printer->Print("\"$oneof_name$\", ",
"oneof_name", "oneof_name",
...@@ -429,7 +430,10 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) { ...@@ -429,7 +430,10 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
printer->Outdent(); printer->Outdent();
printer->Print("}\n\n"); printer->Print("}\n\n");
// TODO(jonskeet): ToString. printer->Print(
"public override string ToString() {\n"
" return pb::JsonFormatter.Default.Format(this);\n"
"}\n\n");
} }
void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer) { void MessageGenerator::GenerateMessageSerializationMethods(io::Printer* printer) {
......
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