-
Notifications
You must be signed in to change notification settings - Fork 636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve Core.Data Serialization and Add Remember Node #14111
Conversation
@saintentropy this build is broken. |
The build failed because Jenkins could not get the correct LibG nuget package with the json (de)serializaition method, which is actually not available yet. |
[NodeName("Remember")] | ||
[NodeDescription(nameof(Properties.Resources.RememberDescription), typeof(Properties.Resources))] | ||
[NodeCategory("Core.Data")] | ||
[InPortNames(">")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Node is designed to look like a watch node with ">" port strings
src/Libraries/CoreNodes/Data.cs
Outdated
#if NET6_0_OR_GREATER | ||
[SupportedOSPlatform("windows")] | ||
#endif | ||
private class ImageConverter : JsonConverter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps this should be called PNGImageConverter?
{ | ||
private string cache = ""; | ||
|
||
[JsonProperty("Cache")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do you need this when the property has the same name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch
{ | ||
switch (e.PropertyName) | ||
{ | ||
case "State": |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nameof?
} | ||
} | ||
|
||
if (jObject.ContainsKey("typeid")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whats the difference between serializations that contain "typeid" and "$typeid" ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In ASP json that us $typeid
as reserved keyword for the type. In the changeset world they used typeid
src/Libraries/CoreNodes/Data.cs
Outdated
|
||
case "dynamo.data:location-1.0.0": | ||
return DynamoUnits.Location.ByLatitudeAndLongitude( | ||
(double)jObject["Latitude"], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
curious - what are your thoughts about this vs adding protected or internal json constructors and just letting newtonsoft figure it out?
I'm fine with this for now, just wanted to evaluate the +/-s
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that is better approach. I was waiting for the .NET7 so we could introduce an interface to support serialization registration similar to the IGraphicItem. (static abstract is now supported in interfaces NET7 and above). I can do this now without the interface for the internal types just to set the pattern.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah I see, so we'll have to wait on the interface approach until after we move to .net 8... I guess you could do it now and change the language target of this csproj to 11?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would the interface look like?
// Verify objects match when serializing / de-serializing geometry type | ||
AssertPreviewValue("abb39e07-db08-45cf-9438-478defffbf68", true); | ||
|
||
// Verify current failure cases are all false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this comment.
} | ||
|
||
[Test] | ||
[Category("Failure")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this fail? Can you add a TODO, task number etc?
} | ||
|
||
[Test] | ||
[Category("Failure")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this fail - etc
} | ||
|
||
[Test] | ||
[Category("Failure")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why does this fail
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Failures are waiting for the upstream PR in LibG
[Category("UnitTests")] | ||
public void RoundTripForColorReturnsSameResult() | ||
{ | ||
var color = DSCore.Color.ByARGB(25, 30, 35, 40); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there any benefit to the preceding tests being implemented using graphs instead of implementing them to how these tests are written without involving graph deserialization? I find them easier to debug. The graph ones in my opinion are opaque.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah... Sorry I was just following the pattern of the other tests for Json. Also added benefit of much greater coverage. We can create a follow up task and pull all the tests into unit test style if it is critical
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nah, not critical, maybe just preferred for future tests.
public static bool CanObjectBeCached(object inputObject) | ||
{ | ||
if (inputObject == null | ||
|| (inputObject is string inputString && inputString == "null") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when do we have a "null" string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about an empty string?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty string is ok :-)
|
||
[Test] | ||
[Category("UnitTests")] | ||
public void CanObjectBeCachedRejectsEmptyString() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this test seems to have the wrong name.
@@ -0,0 +1,4 @@ | |||
## Remember - Documentation | |||
This documentation file is auto generated by NodeDocumentationMarkdownGenerator, Version=2.13.0.2212, Culture=neutral, PublicKeyToken=null. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this seems like a great node to supply better documentation for.
FYI @hwahlstrom
@@ -141,6 +141,12 @@ | |||
<data name="EnumDateOfWeekWednesday" xml:space="preserve"> | |||
<value>Wednesday</value> | |||
</data> | |||
<data name="Exception_Deserialize_Unsupported_Type" xml:space="preserve"> | |||
<value>The stored data can not be rebuilt.</value> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loaded?
} | ||
catch(Exception ex) | ||
{ | ||
throw new NotSupportedException(string.Format(Properties.Resources.Exception_Serialize_Unsupported_Type, inputObject.GetType().FullName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would it be helpful to also log the real exception to the console or concat it?
} | ||
} | ||
|
||
private class ColorConveter : JsonConverter |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why use a bunch of converters for this instead of decorating the class with attributes. I guess I get a bit of a code smell when the serialization and deserialization are handled in different places, one using a converter and one using regular constructors. Can it be unified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the main reason is the overlap with the node -> file serialization and the node object -> data serialization. I hope to introduce the interface so that it can be explicit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind.... file stuff doesn't apply to ZT. But would be handled by the interface
resultAst.Add(AstFactory.BuildAssignment(functionCallIndent, functionCall)); | ||
|
||
//Next add the first key value pair to the output port | ||
var getFirstKey = AstFactory.BuildFunctionCall(BuiltinDictionaryTypeName, BuiltinDictionaryGet, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the ASTFactory somewhere has methods for building these calls easily.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not that I am aware of or could find. The main thing we do here is that we need to pass the data from the inputPort and the cached string into and back out of the node every time. The dictionary is used so that we can only present to the ports data on the node and keep the cache data internal. I think we look long and hard at the time we wrote this and didn't have any out of the box options of dictionaries in ASTFactory.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
look for:
DictionaryExpressionBuilder
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh. Thanks. I did look at that. Interesting but not exactly what we need. The dictionary is already set up by the function we call Remember()
as the return type. The AST construction we are doing is to look up specific keys later... More specifically DictionaryBuilder
embeds the constuctor DesignScript.Builtin.Dictionary.ByKeysValues
whereas we need the method DesignScript.Builtin.Dictionary.ValueAtKey
. There could be a helper for us but not this one.
# Conflicts: # src/Libraries/CoreNodeModels/CoreNodeModelsImages.resx # src/Libraries/CoreNodeModels/Properties/Resources.en-US.resx # src/Libraries/CoreNodeModels/Properties/Resources.resx # test/Tools/docGeneratorTestFiles/sampledictionarycontent/Dynamo_Nodes_Documentation.json
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@saintentropy lets ship it.
Cool... Will merge when CICD confirms |
OK Passed on https://master-15.jenkins.autodesk.com/view/DYN/job/DYN-DevCI_Self_Service/1244/. Going to merge 😃 |
Purpose
The purpose of this PR is to improve the implementation of the Data.ParseJson and Data.StringifyJson to support json serialization of most Dynamo OOB data types. Today these Data nodes fail to serialize or later to deserialize most Objects vs Value types. Geometry for example always fails in StringifyJson. This PR will add support for the following Data found in Dynamo OOB.
ProtoGeometry types with the exception of Topology (as Topology does not make as much sence in isolation from the source)
- Primitive Types (BoundingBox, CoordinateSystem, Plane, Vector, Arc, Circle, Ellipse, EllipseArc, Line, NurbsCurve, PolyCurve, Polygon, Point, and NurbsSurface) are supposed via schema defined in Autodesk Schema Service
- Dynamo Geometry Types (Rectangle, Cuboid, Cone, Cylinder, Sphere, and Mesh) are supported via Dynamo specific schema.
- Complex Geometry Types (PolySurface, Helix, Curves, Surfaces, Solids, TSplines) are supported via SAB and TSM
Dynamo Core Types including Color, Location and Image (via png)
This PR also moves the implementation of the Remember node from the Generative Design package to Core. The implementation of the Remember Node is identical but the serialization format has been updated to use Data.ParseJson and Data.StringifyJson. The version in GD will be marked as deprecated and removed from the package in future releases. Due to the format change we are not migrating the node.
Declarations
Check these if you believe they are true
*.resx
filesRelease Notes
Data.ParseJson and Data.StringifyJson nodes have been improved to support serialization of most Dynamo standard data types. This includes all Geometry types except Topology and Color, Location and Image.
Data.Remember node has been moved from the GenerativeDesign package to Dynamo core library. The new node has improved accuracy in how it stores and restores data.
Reviewers
@sm6srw @QilongTang Please assign
FYIs
@mjkkirschner @twastvedt @LongNguyenP