JSON Deserialization in RhinoCommon

I’ve looked around at some existing threads… but I can’t quite find what I’m after. Apologies if I’ve overlooked something in these:

I want to add functionality to a GH plugin that will automatically deserialize JSON of the form:

{
  "version": 10000,
  "archive3dm": 70,
  "opennurbs": -1912573523,
  "data": "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"
}

into a Rhino object.

The first link suggests explicitly providing the type to JsonConvert.DeserializeObject(x); but this doesn’t work if I don’t know the type in the first place.
The other links/examples seem to be using the Rhino3dmIO toolkit - is this appropriate though for a plugin running inside an active instance of Rhino? I think I want something like CommonObject.Decode but I can’t seem to find it in RhinoCommon. Do I need to add a reference to Rhino3dmIO as well (this seems strange and also results in a bunch of namespace conflicts so I assume this is not the way!) I could do as @Dani_Abalde suggests in the third link, but I think this is slightly different than the opennurbs way (although perhaps the “data” property there is just a string encoding of the same byte array?)

Ideally I’d like to simply configure JSON.Net to automatically deserialize to Rhino geometry wherever it finds it - but I’m willing to do something a bit more “special case” if necessary.

Any pointers?

2 Likes

@dale or @stevebaer ?

if you control the serialisation process too, you could use typenamehandling

Hi @andheum,

You might have a look at the RhinoCompute.cs file found at the bottom of this page:

https://www.rhino3d.com/compute

Rhino.Compute clients reference Newtonsoft.Json to serialize and deserialize Rhino3dm object.

– Dale

Thanks for the quick reply @dale. I’ve looked through RhinoCompute.cs a bit but I confess I’m still at a loss… not sure how to apply what I am seeing. I saw a few examples of DeserializeObject in use but they all already have a type argument specified. There’s also some business with custom JsonConverters in the python code but I’m not sure what I’m looking at there either. Can you give me a little more direction?

To clarify, I was hoping there was some pre-existing method to pop out a GeometryBase from its serialized representation without knowing the geometry type in advance. Perhaps I’m mistaken in thinking this is possible?

I can probably whip up some sample code for this tomorrow

1 Like

:pray: you’re the besttt

I see the problem you are dealing with. GeometryBase and CommonObject are abstract which is causing problems with Json.net deserializer. When you know the exact geometry type you are deserializing to (which compute does know), it is easy enough to just directly use the JsonConvert class

var c = new Rhino.Geometry.Circle(12);
var extrusion = Rhino.Geometry.Extrusion.Create(c.ToNurbsCurve(), 12, true);
var json = Newtonsoft.Json.JsonConvert.SerializeObject(extrusion);
var geometry = Newtonsoft.Json.JsonConvert.DeserializeObject<Rhino.Geometry.Extrusion>(json);

I’ll try to figure out a generic solution for this tomorrow (this time I really mean tomorrow :slight_smile: )

1 Like

Exactly. I can write a thing that repeatedly tries to deserialize it to a bunch of diff geometry (is it a curve? is it a brep? is it a mesh? etc) but it seems like there must be a more elegant way.

Brute forcing it now, with something like this:

but weirdly, a serialized mesh will cast to PolylineCurve, and IsValid returns true! then it tries to handover the mesh-as-polyline and I get this:
image

Coming up blank on a generic solution. I can give you a unsafe and hack technique to do this involving pInvokes and reflection if you really want to try going that route.

I think I’ll need to add some functionality to RhinoCommon itself to improve the situation. Some sort of static function on Rhino.Runtime.CommonObject that creates an instance given a json string.

1 Like

I’m in the process of adding a couple functions to Rhino.Runtime.CommonObject that would look like

public static CommonObject FromBase64String(int archive3dm, int opennurbs, string data);
public static CommonObject FromJSON(Dictionary<string,string> json);

Do you think those would work for you? You should be able to just use JsonConvert.DeserializeObject<Dictionary<string,string>> and pass that to the FromJSON function.

2 Likes

that would be awesome! in the meantime I’m wrapping the objects with type information when I serialize them, and extracting the type info to deserialize accordingly, which works, but this seems cleaner.

Guess I might as well throw a ToJSON function in there as well for good measure.

2 Likes

Done; these functions will be available in the next V7 WIP as well as the next Rhino3dmIO build on nuget.

9 Likes

Good morning @stevebaer,

I’m trying to do some dynamic To/From JSON serializing with Rhino and I don’t quite understand what data the FromJSON method requires (Why a dictionary when the ToJSON just gives a json string? Are there some handy dandy bits of data you can add in to help?). I also couldn’t find a sample anywhere, would you be able to knock one together if there isn’t one, possibly something that might assist me or @andheum with our dynamic serializing needs? :sunglasses:

– Callum

I’m not at my computer today, so I can’t give a great answer.

Try running ToJson on some geometry. This is what FromJson is expecting. It is probably the dictionary that you would get if Json.net converted the string to a dictionary.

1 Like

Here is how it is used in compute

Thanks @Steve for the tip,
I tried it out in Grasshopper, and formalised it a bit better here. But for anyone reading this topic, the below code works magically on a random set of Curves/Breps.

using System.Collections.Generic;
using System;

using Newtonsoft.Json;

namespace RhinoSerialization
{
	public static class ToAndFrom
	{

		public bool CastTo(GeometryBase inthing, out string json)
		{
			Rhino.FileIO.SerializationOptions so = new Rhino.FileIO.SerializationOptions();
			json = inthing.ToJSON(so);
		}

		public bool CastFrom(string json, out GeometryBase outthing)
		{
			Dictionary<string, string> jsonDict = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
			outthing = Rhino.Runtime.CommonObject.FromJSON(jsonDict);
		}

	}
}
6 Likes