Home > .net > ProtoBuf-net: A helper method

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;
}
Advertisements
Categories: .net
  1. April 20, 2010 at 12:42 pm

    Useful, but note that it omits several subtle cases. If you want comparison to the C++ version, then Jon Skeet’s port might be useful (protobuf-net exposes a different API). Also, note that “v2” offers much richer runtime metadata access.

  1. April 20, 2010 at 1:00 pm
  2. May 21, 2010 at 9:31 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: