(de)serialize nested classes GH_GeometricGoo

I have a plugin with a class that contains as a field a list of objects of the class type itself (think of them as children) that I don’t know how to (de)serialize.

To clarify with an example, take the Boat.cs example from here:

Now, suppose the BoatShell class can describe also a boat carrier (a boat that carries other boats) and its fields are:

public class BoatShell
  {
    #region fields
    private double m_width;
    private double m_length;
    private Point3d m_mast;
    private Brep m_shape;
    private Curve m_waterLine;
    private List<BoatShell> m_children; // < list of "children" BoatShells
    #endregion
...

Omitting the rest of the code for simplicity, but suppose there’s a related property, a constructor that accepts also a List<BoatShell> as a parameter, etc… How can one implement (de)serialization inside the Read and Write methods in BoatShellGoo for the m_children field?

Yikes, that sounds like a nightmare to get ones head around! Could you do something like this, by going through the list of children and accessing the members explicitly and writing them into flat lists, then do the reverse when reading them? It might be overly convoluted however especially if you are going to have many boats… maybe someone with better knowledge than me can help:

    public class BoatShellGoo : GH_Goo<BoatShell>
    {

        public BoatShellGoo()
        {
            this.Value = new BoatShell();
        }

        // all the other goo stuff here

        public override bool Write(GH_IO.Serialization.GH_IWriter writer)
        {
            if (Value != null)
            {
                for (int i=0; i<Value.m_children[i].Count; i++)
                {
                     Value.m_children[i].m_mast.Write(writer.CreateChunk("mastData"+i));
                     Value.m_children[i].m_shape.Write(writer.CreateChunk("shapeData"+i));
                     etc...
                }
            }
            
            return true;
        }

Thank you John, I thought about going the thorough way myself, but the problem is that my “Boat” has a lot more fields in need of serialization than just a m_mast and m_shape… not an impossible problem, I guess it’s just a matter of elbow grease at this point. I really hoped for a more elegant solution, as in a way to recursively call read and write - which would have allowed the implementation of more nesting levels.

Anyways, you have already given me some ideas to try out, thank you very much!

maybe someone with better knowledge than me can help

Who, God? :grinning_face_with_smiling_eyes:

hi @ale2x72, did you find a good solution to this other than the above mentioned?

I’m in the works of trying to make a generic writer (as generic as the binary formatter but more in a grasshopper fashion) see below

public static bool WriteAllValueBased(IGH_Goo o, GH_IWriter writer)
        {
            // Set all int/double/bool/string // others+??? plane?
            foreach (var prop in o.GetPublicValueProperties())
            {
                // Int
                if (prop.PropertyType == typeof(int))
                {
                    writer.SetInt32(prop.Name, (int)prop.GetValue(o));
                }
                // Double
                else if (prop.PropertyType == typeof(double))
                {
                    writer.SetDouble(prop.Name, (double)prop.GetValue(o));
                }
                // Bool
                else if (prop.PropertyType == typeof(bool))
                {
                    writer.SetBoolean(prop.Name, (bool)prop.GetValue(o));
                }
                // String
                else if (prop.PropertyType == typeof(string))
                {
                    writer.SetString(prop.Name, (string)prop.GetValue(o));
                }
                // BoundingBox
                else if (prop.PropertyType == typeof(BoundingBox))
                {
                    BoundingBox b = (BoundingBox)prop.GetValue(o);
                    writer.SetBoundingBox(prop.Name, new GH_BoundingBox(b.Min.X, b.Min.Y, b.Min.Z, b.Max.X, b.Max.Y, b.Max.Z));
                }
                // Guid
                else if (prop.PropertyType == typeof(Guid))
                {
                    writer.SetGuid(prop.Name, (Guid)prop.GetValue(o));
                }
                // Int[]
                else if (prop.PropertyType == typeof(int[]))
                {
                    var arr = (int[])prop.GetValue(o);
                    for (int i = 0; i < arr.Length; i++)
                    {
                        writer.SetInt32(prop.Name, arr[i], i);
                    }
                }
                // Double[]
                else if (prop.PropertyType == typeof(double[]))
                {
                    writer.SetDoubleArray(prop.Name, (double[])prop.GetValue(o));
                }
                // Bool[]
                else if (prop.PropertyType == typeof(bool[]))
                {
                    var arr = (bool[])prop.GetValue(o);
                    for (int i = 0; i < arr.Length; i++)
                    {
                        writer.SetBoolean(prop.Name, i, arr[i]);
                    }
                }
                // String[]
                else if(prop.PropertyType == typeof(string[]))
                {
                    var arr = (string[])prop.GetValue(o);
                    for (int i = 0; i < arr.Length; i++)
                    {
                        writer.SetString(prop.Name, i, arr[i]);
                    }
                }
                


                else
                {
                    return false;
                }
            }
            return true;
        }


public virtual IEnumerable<PropertyInfo> GetPublicValueProperties(object o = null)
        {
            o = o ?? this;
            PropertyInfo[] props = o.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);

            foreach (var prop in props)
            {
                if (prop.PropertyType.IsValueType)
                {
                    yield return prop;
                }
                //if(prop.PropertyType == typeof(string) || 
                //    prop.PropertyType == typeof(int) ||
                //    prop.PropertyType == typeof(double) || 
            }

        }

    public virtual bool Write(GH_IWriter writer)
        {

            writer.SetByteArray("fullItem", ObjectToByteArray(this));

            //Write all value based.
            WriteAllValueBased(this, writer);

            //Write all the nested objects
            for (int i = 0; i < Objects.Count; i++)
            {
                Objects[i].Write(writer.CreateChunk("subObjects", i));
            }
}
1 Like

Hi @sonderskovmathias , no, I haven’t found any other solution. On the other hand, your generic writer seems like a promising way to go!

I didnt include all possible types, and I most likely won’t. but it’s a start.