{"id":7522,"date":"2019-11-03T20:46:04","date_gmt":"2019-11-03T20:46:04","guid":{"rendered":"http:\/\/putridparrot.com\/blog\/?p=7522"},"modified":"2019-11-03T20:46:04","modified_gmt":"2019-11-03T20:46:04","slug":"json-within-subclasses-across-multiple-programming-languages","status":"publish","type":"post","link":"https:\/\/putridparrot.com\/blog\/json-within-subclasses-across-multiple-programming-languages\/","title":{"rendered":"JSON within subclasses, across multiple programming languages"},"content":{"rendered":"<p>I was developing an expression tree on a project, i.e. made up of subclasses of the class Expression, such as AndExpression, MemberExpression, LiteralExpression etc. The main code is TypeScript\/JavaScript but this needs to pass JSON to TypeScript\/JavaScript, C# or Java code (and possibly other languages). <\/p>\n<p>Now when JavaScript JSON.stringify does it&#8217;s thing we&#8217;re left with no type information making it problematic converting each type back to it&#8217;s actual type, i.e. to an AndExpression not just an Expression.<\/p>\n<p>A relatively easy way to solve this, whilst not as elegant as one might hope is to store a string representing the type within the object, for example<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nexport class LiteralExpression extends Expression {\r\n  public readonly $type: string = &quot;LiteralExpression&quot;\r\n}\r\n<\/pre>\n<p>When we run the expression through JSON.stringify we get JSON with <em>&#8220;$type&#8221;:&#8221;AndExpression&#8221;<\/em> for example. In JavaScript we still need to do some work to convert this back to JavaScript classes, it&#8217;s easy enough to use <em>JSON.parse(json)<\/em> then iterate over our expression objects converting to subclasses and revive our objects from JSON in this way.<\/p>\n<p><em>Note the use of the variable name <strong>$type<\/strong>. It&#8217;s important that it&#8217;s named <strong>$type<\/strong> if you want it to easily be translated into C# objects with Json.NET as this name is hard coded in this library, whereas Java&#8217;s jackson JAR allows us to easily change the name\/key used.<\/em><\/p>\n<p><strong>Json.NET (Newtonsoft.Json)<\/strong><\/p>\n<p>Sadly we don&#8217;t quite get everything for free using Json.NET because it&#8217;s expecting C# style naming for classes, i.e. assembly\/namespace etc. The easiest way to deal with this it to serialize\/deserialize using our own SerializationBinder, for example<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\npublic static class Serialization\r\n{\r\n  public class KnownTypesBinder : ISerializationBinder\r\n  {\r\n    public IList&lt;Type&gt; KnownTypes { get; set; }\r\n\r\n    public Type BindToType(string assemblyName, string typeName)\r\n    {\r\n      return KnownTypes.SingleOrDefault(t =&gt; t.Name == typeName);\r\n    }\r\n\r\n    public void BindToName(Type serializedType, out string assemblyName, out string typeName)\r\n    {\r\n      assemblyName = null;\r\n      typeName = serializedType.Name;\r\n    }\r\n  }\r\n\r\n  private static KnownTypesBinder knownTypesBinder = new KnownTypesBinder\r\n  {\r\n    KnownTypes = new List&lt;Type&gt;\r\n    {\r\n      typeof(AndExpression),\r\n      typeof(BinaryExpression),\r\n      typeof(LiteralExpression),\r\n      typeof(LogicalExpression),\r\n      typeof(MemberExpression),\r\n      typeof(NotExpression),\r\n      typeof(OperatorExpression),\r\n      typeof(OrExpression)\r\n    }\r\n  };\r\n\r\n  public static string Serialize(Expression expression)\r\n  {\r\n    var json = JsonConvert.SerializeObject(\r\n      expression, \r\n      Formatting.None, \r\n      new JsonSerializerSettings\r\n    {\r\n      TypeNameHandling = TypeNameHandling.Objects,\r\n      SerializationBinder = knownTypesBinder\r\n    });\r\n    return json;\r\n  }\r\n\r\n  public static Expression Deserialize(string json)\r\n  {\r\n    return JsonConvert.DeserializeObject&lt;Expression&gt;(\r\n      json, \r\n      new JsonSerializerSettings\r\n    {\r\n      TypeNameHandling = TypeNameHandling.Objects,\r\n      SerializationBinder = knownTypesBinder\r\n    });\r\n  }\r\n}\r\n<\/pre>\n<p>Don&#8217;t forget you&#8217;ll also need to mark your properties and\/or constructor parameters with <em>[JsonProperty(&#8220;left&#8221;)]<\/em> especially if you have situations where the names are keywords.<\/p>\n<p><strong>com.fasterxml.jackson.core<\/strong><\/p>\n<p>In Java we can add the following dependency to our pom.xml<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\n&lt;dependency&gt;\r\n  &lt;groupId&gt;com.fasterxml.jackson.core&lt;\/groupId&gt;\r\n  &lt;artifactId&gt;jackson-databind&lt;\/artifactId&gt;\r\n  &lt;version&gt;2.10.0&lt;\/version&gt;\r\n&lt;\/dependency&gt;\r\n<\/pre>\n<p>Now in our Expression base class we write the following<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\nimport com.fasterxml.jackson.annotation.JsonTypeInfo;\r\n\r\n@JsonTypeInfo(\r\n  use = JsonTypeInfo.Id.NAME, \r\n  include = JsonTypeInfo.As.PROPERTY, \r\n  property = &quot;$type&quot;)\r\npublic class Expression {\r\n}\r\n<\/pre>\n<p>This tells jackson to include the type info using the keyword $type. <\/p>\n<p>We also need to add the @JsonCreator annotation to each of our classes and each constructor parameter requires the following annotation <em>@JsonProperty(&#8220;left&#8221;)<\/em>. Finally to serialize\/deserialize we create an ObjectMapper to allow us to map our types to real objects using<\/p>\n<pre class=\"brush: java; title: ; notranslate\" title=\"\">\r\npackage com.rbs.expressions;\r\n\r\nimport com.fasterxml.jackson.core.JsonProcessingException;\r\nimport com.fasterxml.jackson.databind.ObjectMapper;\r\nimport com.fasterxml.jackson.databind.jsontype.NamedType;\r\n\r\npublic class Serialization {\r\n\r\n  private static ObjectMapper createMapper() {\r\n    ObjectMapper mapper = new ObjectMapper();\r\n\r\n    mapper.registerSubtypes(\r\n      new NamedType(AndExpression.class, &quot;AndExpression&quot;),\r\n      new NamedType(BinaryExpression.class, &quot;BinaryExpression&quot;),\r\n      new NamedType(OrExpression.class, &quot;OrExpression&quot;),\r\n      new NamedType(LiteralExpression.class, &quot;LiteralExpression&quot;),\r\n      new NamedType(LogicalExpression.class, &quot;LogicalExpression&quot;),\r\n      new NamedType(MemberExpression.class, &quot;MemberExpression&quot;),\r\n      new NamedType(NotExpression.class, &quot;NotExpression&quot;),\r\n      new NamedType(OperatorExpression.class, &quot;OperatorExpression&quot;)\r\n    );\r\n\r\n    return mapper;\r\n  }\r\n\r\n  public static String serialize(Expression expression) throws JsonProcessingException {\r\n    return createMapper().writeValueAsString(expression);\r\n  }\r\n\r\n  public static Expression deserialize(String json) throws JsonProcessingException {\r\n    return createMapper().readValue(json, Expression.class);\r\n  }\r\n}\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>I was developing an expression tree on a project, i.e. made up of subclasses of the class Expression, such as AndExpression, MemberExpression, LiteralExpression etc. The main code is TypeScript\/JavaScript but this needs to pass JSON to TypeScript\/JavaScript, C# or Java code (and possibly other languages). Now when JavaScript JSON.stringify does it&#8217;s thing we&#8217;re left with [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[3,161,45],"tags":[],"class_list":["post-7522","post","type-post","status-publish","format-standard","hentry","category-c","category-java","category-javascript"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7522","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/comments?post=7522"}],"version-history":[{"count":2,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7522\/revisions"}],"predecessor-version":[{"id":7594,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/posts\/7522\/revisions\/7594"}],"wp:attachment":[{"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/media?parent=7522"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/categories?post=7522"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/putridparrot.com\/blog\/wp-json\/wp\/v2\/tags?post=7522"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}