I’m trying to populate some userData in a Mesh, serialize the Mesh to JSON, and then deserialize it back into a Mesh and read back out the userData, using Rhino3dm (nuget 7.15.0). I kept not being able to find my userData object in the deserialized Mesh, and as I dig in, it appears it’s not even being added to the original mesh. Here is my code to reproduce:
using System;
namespace RhinoUserDataTest
{
[System.Runtime.InteropServices.Guid("fbaca1a5-5282-41e0-b5ba-47566db455d9")]
public class TestUserData : Rhino.DocObjects.Custom.UserData
{
public int TestNumber { get; set; }
public TestUserData() { }
public TestUserData(int testNumber)
{
TestNumber = testNumber;
}
public override string Description => "Test UserData";
public override string ToString() => String.Format("testNumber={0}", TestNumber);
protected override void OnDuplicate(Rhino.DocObjects.Custom.UserData source)
{
TestUserData src = source as TestUserData;
if (src != null)
TestNumber = src.TestNumber;
}
public override bool ShouldWrite => true;
protected override bool Read(Rhino.FileIO.BinaryArchiveReader archive)
{
Rhino.Collections.ArchivableDictionary dict = archive.ReadDictionary();
TestNumber = (int)dict[nameof(TestNumber)];
return true;
}
protected override bool Write(Rhino.FileIO.BinaryArchiveWriter archive)
{
var dict = new Rhino.Collections.ArchivableDictionary(1, "TestUserData");
dict.Set(nameof(TestNumber), TestNumber);
archive.WriteDictionary(dict);
return true;
}
}
internal class Program
{
public static void Main(string[] args)
{
var originalMesh = new Rhino.Geometry.Mesh();
originalMesh.Vertices.Add(0, 0, 0);
originalMesh.Vertices.Add(1, 0, 0);
originalMesh.Vertices.Add(1, 1, 0);
originalMesh.Faces.AddFace(0, 1, 2);
var tUD = new TestUserData(42);
Console.WriteLine($"originalMesh.UserData.Count (before add): {originalMesh.UserData.Count}");
originalMesh.UserData.Add(tUD);
Console.WriteLine($"originalMesh.UserData.Count (after add): {originalMesh.UserData.Count}");
var json = originalMesh.ToJSON(new Rhino.FileIO.SerializationOptions(){RhinoVersion = 7, WriteUserData = true});
var deserializedMesh = Rhino.Geometry.Mesh.FromJSON(json);
Console.WriteLine($"deserializedMesh.UserData.Count: {deserializedMesh.UserData.Count}");
}
}
}
When run, this outputs:
originalMesh.UserData.Count (before add): 0
originalMesh.UserData.Count (after add): 0
deserializedMesh.UserData.Count: 0
I tried stepping into Mesh.UserData.Add, which JetBrains Rider tells me looks like this:
public bool Add(UserData userdata)
{
if (!(userdata is SharedUserDictionary))
{
Type type = userdata.GetType();
ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes);
if (!type.IsPublic || constructor == (ConstructorInfo) null)
throw new ArgumentException("user-data must be a public class and have a parameterless constructor");
}
bool flag = UnsafeNativeMethods.ON_Object_AttachUserData(this.m_parent.ConstPointer(), userdata.NonConstPointer(true), true);
if (flag)
UserData.StoreInRuntimeList(userdata);
return flag;
}
When I step through that method, it claims to be following the logic path that leads to the ArgumentException being thrown, despite debugger telling me that type.IsPublic = true
and constructor != null
. However, the runtime does not actually bubble up an exception to trap. I’m guessing the source code I’m being shown is not the actual executing source…?
Any clues on what I’m doing wrong? Why would UserDataList.Add(…) fail on me? Is reading / writing UserData values from Rhino3dm not supported?