ProtoBuf-net: A helper method
The current implementation of protobuf-net does not contain a simple way of writing the proto as a string, or key-value pairs (unlike the C++ library).
Here’s a simple extension method to aid in logging protos, in case you ever have a similar need.
///
/// Returns the contents of the proto object log-friendly string format.
/// This method will also cycle through the repeating groups.
/// Eg: MyProto { fielda [100] = Foo, fieldb [101] = bar }
///
public static string ToDebug(this IExtensible proto) {
StringBuilder sb = new StringBuilder();
sb.AppendFormat("{0} {{", proto.GetType().Name);
ReadProto(proto, sb);
sb.Append("}");
return sb.ToString();
}
private static void ReadProto(IExtensible proto, StringBuilder sb) {
object val;
SortedDictionary props = GetProtoAttributeProperties(proto);
int[] keys = new int[props.Count];
PropertyInfo info = null;
MethodInfo mi = null;
props.Keys.CopyTo(keys, 0);
for (int i = 0; i < props.Keys.Count; i++) {
info = props[keys[i]];
val = info.GetValue(proto, null);
if (val == null)
continue;
mi = proto.GetType().GetMethod(
string.Format("ShouldSerialize{0}", props[keys[i]].Name),
BindingFlags.NonPublic | BindingFlags.Instance);
if (mi != null && !Convert.ToBoolean(mi.Invoke(proto, null)))
continue;
sb.AppendFormat("{0} [{1}] = {2}",
info.Name, keys[i],
val == null ? string.Empty :
val is IExtensible ? val.GetType().Name : val.ToString());
if (val is IExtensible) {
sb.Append(" {");
ReadProto((IExtensible)val, sb);
sb.Append("}");
} else {
IList list = val as IList;
if (list != null) {
ReadRepeatingGroups(list, sb);
} else if (i != keys.Length - 1)
sb.Append(", ");
}
}
}
private static void ReadRepeatingGroups(IList list, StringBuilder sb) {
if (list == null)
return;
if (list.Count == 0) {
sb.Append("{}");
return;
}
sb.Append(" {");
for (int i = 0; i < list.Count; i++) {
sb.Append(" {");
ReadProto((IExtensible)list[i], sb);
sb.Append("}");
if (i != list.Count - 1)
sb.Append(", ");
}
sb.Append("}");
}
private static SortedDictionary GetProtoAttributeProperties(IExtensible proto) {
SortedDictionary props = new SortedDictionary();
PropertyInfo[] properties = proto.GetType().GetProperties();
foreach (PropertyInfo pi in properties) {
if (!pi.IsDefined(typeof(ProtoBuf.ProtoMemberAttribute), false))
continue;
ProtoMemberAttribute attr = null;
object[] attributes = pi.GetCustomAttributes(false);
foreach (object a in attributes) {
attr = a as ProtoMemberAttribute;
if (attr != null)
break;
}
if (attr != null)
props.Add(attr.Tag, pi);
}
return props;
}