Elements: Elements objects cannot be serialised to JSON with standard JSON libraries

Went down a rabbit hole when one of our services was returning inexplicably truncated JSON responses, below is the developer’s journey of debugging this, please bear with me @ikeough 🤓

In Microservices land

We have our own object data model for a Building and an endpoint in a service (ASP .NET Core 2.2) that generates a new one.

Using the default serialiser settings that come with ASP .NET Core, was finding that JSON responses are truncated producing invalid responses.

StackOverflow suggested using these options on the serialiser options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore, but this makes the service go into a loop and the response is a nice infinite JSON. At this point I thought our own objects were at fault, no exceptions are thrown to hint that it goes into a self-referencing loop when serialising.

In libraries

Moved to testing this in a library context. Added JsonIgnore attributes for computed properties that referenced another, that wasn’t it.

Finally, when serialising using Newtonsoft, we get this error :

Self referencing loop detected for property 'Face' with type 'Elements.Geometry.Solids.Face'. Path 'Floors[0].FloorZone.geometry[0].Faces.0.Outer'.

Getting somewhere 😥

Our Building model has an List<Elements.Floor> property, which in turn references a Mass which in turn has Solids, which has Faces that have Loops that reference their parent faces.

image

Reading through the source code of Hypar.Elements, we find out why : https://github.com/hypar-io/elements/blob/fa9353cb1e183595a222436f01ce24a53b3493bb/src/Elements/Serialization/JSON/SolidConverter.cs#L15-L19

Custom converters

Trying to add the Hypar SolidConverter to our serialiser settings is a no-go either, since its constructor wants a weird dictionary of <long, Material> - I have no way of producing that (unless a very expensive crawl of entire object tree keeping track of materials counts ).

In my opinion we should not have to use custom converters for these base types. It’s unclear to me why references to parent objects are stored as public properties on Loop (or any other type) instead of these being private or behind GetParent() methods.

The SolidConverter itself seems to ignore these properties anyway when deserialising a model, which means they are not essential and can be reconstructed/computed.

I might be missing an obvious use case - just can’t think of one.

Proposal

Eliminate self-referencing properties in Elements so that the library can be used with standard serialisation libraries.

The logic of all the custom converters will need to be replicated in languages other than C# when dealing with JSON files coming from Hypar.Elements when you don’t use Hypar’s own C# serialisation library.

Any thoughts ?

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Comments: 15 (15 by maintainers)

Most upvoted comments

Also, the material dictionary hack will most likely become moot in the future because I’m going to take the Material property off of Solid. It turns out that the concept of a material in our domain is more nuanced than just a color and a glossiness. If you look at IfcMaterial as an example, you get into issues about specifying the layers and directionality of materials. This kind of stuff needs to happen at the Element level.

In the mean time, the dictionary that you need is simply the Model.Materials dictionary. This should already have been deserialized at the point at which your converter requires it. No crawling required.