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 Face
s that have Loop
s that reference their parent faces.
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)
Also, the material dictionary hack will most likely become moot in the future because I’m going to take the
Material
property off ofSolid
. 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 atIfcMaterial
as an example, you get into issues about specifying the layers and directionality of materials. This kind of stuff needs to happen at theElement
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.