Serializing objects with IGH_Goo members

Hi all,

I have constructed a class that contains among others a dictionary like this:

[Serializable()]
public class MyObject
{
     public Dictionary<string, IGH_Goo> myDictionary = new Dictionary<string, IGH_Goo>();
     ....
}
[Serializable()]
public class GH_MyObject : Grasshopper.Kernel.Types.GH_GeometricGoo<MyObject>, IGH_PreviewData, IGH_BakeAwareData, GH_ISerializable
{
    ....
}

This way I can relate a key to a Grasshopper entity (GH_Integer, GH_Curve, GH_Point…), let’s say anything wrapped in a IGH_Goo and dynamically modify this dictionary on the go…
Based on this class I managed to pass it around between components as a GH_MyObject and to use it as an input:

pManager.AddParameter(new Param_MyObject() );

Last thing now is to make sure I can Internalize this data at an input node or inside a param-component… It turns out GH_Integer, GH_Curves, etc. are not serializable?

How to make the class I have serializable? What do I forget or what do I not see?
When internalizing these GH_Entity’s in grasshopper, how is the serialization done?

If someone might help me out, thanks!
Have a nice day.

The exception I get can be found underneath:

System.Runtime.Serialization.SerializationException: Type ‘Grasshopper.Kernel.Types.GH_Integer’ in Assembly 'Grasshopper, Version=7.2.21021.7001, Culture=neutral, PublicKeyToken=dda4f5ec2cd80803’ is not marked as serializable.
at System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type)
at System.Collections.Concurrent.ConcurrentDictionary2.GetOrAdd(TKey key, Func2 valueFactory)
at System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context)
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo()
at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArrayMember(WriteObjectInfo objectInfo, NameInfo arrayElemTypeNameInfo, Object data)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteArray(WriteObjectInfo objectInfo, NameInfo memberNameInfo, WriteObjectInfo memberObjectInfo)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header inHeaders, __BinaryWriter serWriter, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header headers, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph)
at GH_MyObject.Write(GH_IWriter writer)

take a look at this thread
it probably works if you replace GH_Structure<T> in my case with your IGH_Goo

1 Like

Thanks Will, it sort of helped indeed. For the sake of completeness I add some more information.

Writing IGH_Goo is indeed possible by just using myIGH_Goo.Write(writer). In order to read it afterwards I looked at how this was done for GH_Structure. One should cast the IGH_Goo as a GH_Entity. Might be that my approach is silly (?). Anyways, this is how I implemented it at the moment.

// myDictionary of type Dictionary<IGH_Goo>

public override bool Write(GH_IWriter writer){
	int n = 0;
	foreach (var pair in this.Value.myDictionary){
		GH_IWriter chunk = writer.CreateChunk("MyDictionary", n++);
		chunk.SetString("Key", pair.Key);
		GH_IWriter myChunk = chunk.CreateChunk("Item");
		pair.Value.Write(myChunk);
	}
	return true; // check for success
}
public override bool Read(GH_IReader reader){
	Dictionary<string, IGH_Goo> myDictionaryRead = new Dictionary<string, IGH_Goo>();
	for (int i = 0; i < int.MaxValue; i++){
		var chunk = reader.FindChunk("MyDictionary", i);
		if (chunk is null) break;
		string key = chunk.GetString("Key");
		GH_IReader treeChunk = chunk.FindChunk("Item");
				
		GH_Types type = treeChunk.Items[0].Type;
		GH_Item gH_Item = treeChunk.Items[0];

		IGH_Goo iGH_Goo = null;
		if (type <= GH_Types.gh_point3d){   // This code was taken from Grasshopper.Kernel.Data.GH_Structure<T>.Read()
			switch (type){
				case GH_Types.gh_bool:
					iGH_Goo = new GH_Boolean(gH_Item._bool);
					break;
				case GH_Types.gh_byte:
					iGH_Goo = new GH_Integer((int)gH_Item._byte);
					break;
				case GH_Types.gh_int32:
					iGH_Goo = new GH_Integer(gH_Item._int32);
					break;
				case GH_Types.gh_int64:
					iGH_Goo = new GH_Number((double)gH_Item._int64);
					break;
				case GH_Types.gh_single:
					iGH_Goo = new GH_Number((double)gH_Item._single);
					break;
				case GH_Types.gh_double:
					iGH_Goo = new GH_Number(gH_Item._double);
					break;
				case GH_Types.gh_decimal:
					iGH_Goo = new GH_Number(Convert.ToDouble(gH_Item._decimal));
					break;
				case GH_Types.gh_date:
					iGH_Goo = new GH_Time(gH_Item._date);
					break;
				case GH_Types.gh_guid:
					iGH_Goo = new GH_Guid(gH_Item._guid);
					break;
				case GH_Types.gh_string:
					iGH_Goo = new GH_String(gH_Item._string);
					break;
				default:
					if (type3 != GH_Types.gh_drawing_color){
						if (type3 == GH_Types.gh_point3d){
							GH_Point3D point3d = gH_Item._point3d;
							iGH_Goo = new GH_Point(new Point3d(point3d.x, point3d.y, point3d.z));
						}
					} else {
						iGH_Goo = new GH_Colour(gH_Item._drawing_color);
					}
					break;
			}
		} else if (type3 <= GH_Types.gh_interval2d) {
			if (type3 != GH_Types.gh_interval1d) {
				if (type3 == GH_Types.gh_interval2d) {
					GH_IO.Types.GH_Interval2D interval2d = gH_Item._interval2d;
					iGH_Goo = new Grasshopper.Kernel.Types.GH_Interval2D(new UVInterval(new Interval(interval2d.u.a, interval2d.u.b), new Interval(interval2d.v.a, interval2d.v.b)));
				}
			} else {
				GH_Interval1D interval1d = gH_Item._interval1d;
				iGH_Goo = new GH_Interval(new Interval(interval1d.a, interval1d.b));
			}
		} else if (type3 != GH_Types.gh_line) {
			if (type3 == GH_Types.gh_plane) {
				GH_IO.Types.GH_Plane plane = gH_Item._plane;
				Point3d arg_471_0 = new Point3d(plane.Origin.x, plane.Origin.y, plane.Origin.z);
				Vector3d vector3d = new Vector3d(plane.XAxis.x, plane.XAxis.y, plane.XAxis.z);
				Vector3d vector3d2 = new Vector3d(plane.YAxis.x, plane.YAxis.y, plane.YAxis.z);
				iGH_Goo = new Grasshopper.Kernel.Types.GH_Plane(new Plane(arg_471_0, vector3d, vector3d2));
			}
		} else {
			GH_IO.Types.GH_Line line = gH_Item._line;
			iGH_Goo = new Grasshopper.Kernel.Types.GH_Line(new Line(line.A.x, line.A.y, line.A.z, line.B.x, line.B.y, line.B.z));
		}
		myDictionaryRead .Add(key, iGH_Goo);
	}
}

@looslennert Did you noticed String when writes creates 2 outputs?
The first one is a bool and the second is the actual string, so the current code here won’t work.
Did you find a different solution for this?
Cheers.