changeset 658:63982c8c294a

merge am-dev -> default
author Alain Mazy <alain@mazy.be>
date Tue, 14 May 2019 18:39:50 +0200
parents 002d9562c8f5 (current diff) 9e3bb8b4f726 (diff)
children 68b5688241d5
files .hgignore Applications/Generic/NativeStoneApplicationRunner.cpp Applications/Wasm/StartupParametersBuilder.cpp Framework/Radiography/RadiographyLayer.cpp Framework/Radiography/RadiographyLayer.h Framework/Radiography/RadiographyScene.h Framework/Radiography/RadiographySceneReader.cpp
diffstat 14 files changed, 295 insertions(+), 90 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue May 14 16:54:13 2019 +0200
+++ b/.hgignore	Tue May 14 18:39:50 2019 +0200
@@ -17,6 +17,8 @@
 Applications/build-*
 CMakeLists.txt.user
 Platforms/Generic/ThirdPartyDownloads/
+Resources/CodeGeneration/.env
+Resources/CodeGeneration/.idea
 Resources/CodeGeneration/__pycache__
 Resources/CodeGeneration/build/
 Resources/CodeGeneration/build_browser/
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp	Tue May 14 16:54:13 2019 +0200
+++ b/Applications/Generic/NativeStoneApplicationRunner.cpp	Tue May 14 18:39:50 2019 +0200
@@ -106,7 +106,7 @@
     {
       boost::program_options::store(
         boost::program_options::command_line_parser(argc, argv).
-          options(options).run(), parameters);
+          options(options).allow_unregistered().run(), parameters);
       boost::program_options::notify(parameters);
     }
     catch (boost::program_options::error& e)
@@ -124,11 +124,6 @@
     if (error || parameters.count("help"))
     {
       std::cout << std::endl
-                << "Usage: " << argv[0] << " [OPTION]..." << std::endl
-                << "Orthanc, lightweight, RESTful DICOM server for healthcare "
-                << "and medical research." << std::endl << std::endl
-                << "Demonstration application of Orthanc Stone in native "
-                << "environment." << std::endl;
 
       std::cout << options << "\n";
       return error ? -1 : 0;
--- a/Applications/Wasm/StartupParametersBuilder.cpp	Tue May 14 16:54:13 2019 +0200
+++ b/Applications/Wasm/StartupParametersBuilder.cpp	Tue May 14 18:39:50 2019 +0200
@@ -3,7 +3,7 @@
 
 namespace OrthancStone
 {
-  void StartupParametersBuilder::Clear()
+      void StartupParametersBuilder::Clear()
   {
     startupParameters_.clear();
   }
@@ -43,11 +43,6 @@
         if(std::get<1>(*it).length() > 0)
           argSs << "=" << std::get<1>(*it);
 
-        argvStrings[argCounter] = argSs.str();
-        cmdLine = cmdLine + " " + argvStrings[argCounter];
-        argv[argCounter] = argvStrings[argCounter].c_str();
-        argCounter++;
-    }
 
     std::cout << "simulated cmdLine = \"" << cmdLine.c_str() << "\"\n";
 
@@ -55,7 +50,7 @@
     {
       boost::program_options::store(
         boost::program_options::command_line_parser(argCounter, argv.data()).
-          options(options).run(), parameters);
+          options(options).allow_unregistered().run(), parameters);
       boost::program_options::notify(parameters);
     }
     catch (boost::program_options::error& e)
--- a/Framework/Radiography/RadiographyLayer.cpp	Tue May 14 16:54:13 2019 +0200
+++ b/Framework/Radiography/RadiographyLayer.cpp	Tue May 14 18:39:50 2019 +0200
@@ -34,6 +34,8 @@
 
   RadiographyLayer::Geometry::Geometry() :
     hasCrop_(false),
+    flipVertical_(false),
+    flipHorizontal_(false),
     panX_(0),
     panY_(0),
     angle_(0),
@@ -57,7 +59,7 @@
 
   void RadiographyLayer::UpdateTransform()
   {
-    transform_ = AffineTransform2D::CreateScaling(geometry_.GetPixelSpacingX(), geometry_.GetPixelSpacingY());
+    transform_ = AffineTransform2D::CreateScaling(geometry_.GetScalingX(), geometry_.GetScalingY());
 
     double centerX, centerY;
     GetCenter(centerX, centerY);
@@ -204,6 +206,21 @@
     BroadcastMessage(RadiographyLayer::LayerEditedMessage(*this));
   }
 
+  void RadiographyLayer::SetFlipVertical(bool flip)
+  {
+    geometry_.SetFlipVertical(flip);
+    UpdateTransform();
+
+    EmitMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
+
+  void RadiographyLayer::SetFlipHorizontal(bool flip)
+  {
+    geometry_.SetFlipHorizontal(flip);
+    UpdateTransform();
+
+    EmitMessage(RadiographyLayer::LayerEditedMessage(*this));
+  }
 
   void RadiographyLayer::SetSize(unsigned int width,
                                  unsigned int height)
--- a/Framework/Radiography/RadiographyLayer.h	Tue May 14 16:54:13 2019 +0200
+++ b/Framework/Radiography/RadiographyLayer.h	Tue May 14 18:39:50 2019 +0200
@@ -64,6 +64,8 @@
       unsigned int       cropY_;
       unsigned int       cropWidth_;
       unsigned int       cropHeight_;
+      bool               flipVertical_;
+      bool               flipHorizontal_;
       double             panX_;
       double             panY_;
       double             angle_;
@@ -155,6 +157,35 @@
         return pixelSpacingY_;
       }
 
+      void SetFlipVertical(bool flip) //  mirrors image around an horizontal axis (note: flip is applied before the rotation !)
+      {
+        flipVertical_ = flip;
+      }
+
+      void SetFlipHorizontal(bool flip) //  mirrors image around a vertical axis (note: flip is applied before the rotation !)
+      {
+        flipHorizontal_ = flip;
+      }
+
+      bool GetFlipVertical() const
+      {
+        return flipVertical_;
+      }
+
+      bool GetFlipHorizontal() const
+      {
+        return flipHorizontal_;
+      }
+
+      double GetScalingX() const
+      {
+        return (flipHorizontal_ ? - pixelSpacingX_: pixelSpacingX_);
+      }
+
+      double GetScalingY() const
+      {
+        return (flipVertical_ ? - pixelSpacingY_: pixelSpacingY_);
+      }
     };
 
   private:
@@ -240,6 +271,10 @@
     void SetPan(double x,
                 double y);
 
+    void SetFlipVertical(bool flip); //  mirrors image around an horizontal axis (note: flip is applied before the rotation !)
+
+    void SetFlipHorizontal(bool flip); //  mirrors image around a vertical axis (note: flip is applied before the rotation !)
+
     void SetResizeable(bool resizeable)
     {
       geometry_.SetResizeable(resizeable);
--- a/Framework/Radiography/RadiographyScene.h	Tue May 14 16:54:13 2019 +0200
+++ b/Framework/Radiography/RadiographyScene.h	Tue May 14 18:39:50 2019 +0200
@@ -151,7 +151,7 @@
 
     void OnTagsReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);
 
-    void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);
+    virtual void OnFrameReceived(const OrthancApiClient::BinaryResponseReadyMessage& message);
     
     void OnDicomExported(const OrthancApiClient::JsonResponseReadyMessage& message);
 
--- a/Framework/Radiography/RadiographySceneReader.cpp	Tue May 14 16:54:13 2019 +0200
+++ b/Framework/Radiography/RadiographySceneReader.cpp	Tue May 14 18:39:50 2019 +0200
@@ -236,5 +236,16 @@
     geometry.SetResizeable(jsonLayer["isResizable"].asBool());
     geometry.SetPan(jsonLayer["pan"]["x"].asDouble(), jsonLayer["pan"]["y"].asDouble());
     geometry.SetPixelSpacing(jsonLayer["pixelSpacing"]["x"].asDouble(), jsonLayer["pixelSpacing"]["y"].asDouble());
+
+    // these fields were introduced later -> they might not exist
+    if (jsonLayer.isMember("flipVertical"))
+    {
+      geometry.SetFlipVertical(jsonLayer["flipVertical"].asBool());
+    }
+    if (jsonLayer.isMember("flipHorizontal"))
+    {
+      geometry.SetFlipHorizontal(jsonLayer["flipHorizontal"].asBool());
+    }
+
   }
 }
--- a/Framework/Radiography/RadiographySceneWriter.cpp	Tue May 14 16:54:13 2019 +0200
+++ b/Framework/Radiography/RadiographySceneWriter.cpp	Tue May 14 18:39:50 2019 +0200
@@ -132,6 +132,9 @@
       output["pixelSpacing"] = pan;
     }
 
+    output["flipVertical"] = geometry.GetFlipVertical();
+    output["flipHorizontal"] = geometry.GetFlipHorizontal();
+
     if (dynamic_cast<const RadiographyTextLayer*>(&layer) != NULL)
     {
       WriteLayer(output, dynamic_cast<const RadiographyTextLayer&>(layer));
--- a/Resources/CodeGeneration/README.md	Tue May 14 16:54:13 2019 +0200
+++ b/Resources/CodeGeneration/README.md	Tue May 14 18:39:50 2019 +0200
@@ -14,4 +14,7 @@
 
 'testWasmIntegrated` contains a small Web app demonstrating the 
 interaction between TypeScript and C++ in WASM.
-source ~/apps/emsdk/emsdk_env.sh
\ No newline at end of file
+source ~/apps/emsdk/emsdk_env.sh
+
+
+Install Python and the following packages `pip install pyyaml jinja2`
--- a/Resources/CodeGeneration/stonegentool.py	Tue May 14 16:54:13 2019 +0200
+++ b/Resources/CodeGeneration/stonegentool.py	Tue May 14 18:39:50 2019 +0200
@@ -69,6 +69,26 @@
         fileContent = JsonHelpers.removeCommentsFromJsonContent(fileContent)
         return json.loads(fileContent)
 
+class FieldDefinition:
+
+    def __init__(self, name: str, type: str, defaultValue: str):
+        self.name = name
+        self.type = type
+        self.defaultValue = defaultValue
+
+    @staticmethod
+    def fromKeyValue(key: str, value: str):
+
+        if "=" in value:
+            splitValue = value.split(sep="=")
+            type = splitValue[0].strip(" ")
+            defaultValue = splitValue[1].strip(" ")
+        else:
+            type = value
+            defaultValue = None
+
+        return FieldDefinition(name = key, type = type, defaultValue = defaultValue)
+
 
 def LoadSchemaFromJson(filePath):
     return JsonHelpers.loadJsonWithComments(filePath)
@@ -129,6 +149,30 @@
 def NeedsCppConstruction(canonTypename):
   return False
 
+def DefaultValueToTs(enums, field:FieldDefinition):
+    tsType = CanonToTs(field.type)
+
+    enumNames = []
+    for enum in enums:
+        enumNames.append(enum['name'])
+
+    if tsType in enumNames:
+        return tsType + "." + field.defaultValue
+    else:
+        return field.defaultValue
+
+def DefaultValueToCpp(root, enums, field:FieldDefinition):
+    cppType = CanonToCpp(field.type)
+
+    enumNames = []
+    for enum in enums:
+        enumNames.append(enum['name'])
+
+    if cppType in enumNames:
+        return root + "::" + cppType + "_" + field.defaultValue
+    else:
+        return field.defaultValue
+
 def RegisterTemplateFunction(template,func):
   """Makes a function callable by a jinja2 template"""
   template.globals[func.__name__] = func
@@ -140,13 +184,16 @@
   RegisterTemplateFunction(template,CanonToTs)
   RegisterTemplateFunction(template,NeedsTsConstruction)
   RegisterTemplateFunction(template,NeedsCppConstruction)
+  RegisterTemplateFunction(template, DefaultValueToTs)
+  RegisterTemplateFunction(template, DefaultValueToCpp)
   return template
 
 def MakeTemplateFromFile(templateFileName):
-  templateFile = open(templateFileName, "r")
-  templateFileContents = templateFile.read()
-  return MakeTemplate(templateFileContents)
-  templateFile.close()
+
+  with open(templateFileName, "r") as templateFile:
+    templateFileContents = templateFile.read()
+    return MakeTemplate(templateFileContents)
+
 
 def EatToken(sentence):
     """splits "A,B,C" into "A" and "B,C" where A, B and C are type names
@@ -363,7 +410,7 @@
   ret = {}
   for k,v in fieldDict.items():
     if k != "__handler":
-      ret[k] = v
+      ret[k] = FieldDefinition.fromKeyValue(k, v)
     if k.startswith("__") and k != "__handler":
       raise RuntimeError("Fields starting with __ (double underscore) are reserved names!")
   return ret
@@ -473,6 +520,9 @@
   with open(fn, 'r', encoding='latin-1') as f:
     schemaText = f.read()
     assert(type(schemaText) == str)
+  return LoadSchemaFromString(schemaText = schemaText)
+
+def LoadSchemaFromString(schemaText:str):
     # ensure there is a space after each colon. Otherwise, dicts could be
     # erroneously recognized as an array of strings containing ':'
     for i in range(len(schemaText)-1):
@@ -483,12 +533,14 @@
           lineNumber = schemaText.count("\n",0,i) + 1
           raise RuntimeError("Error at line " + str(lineNumber) + " in the schema: colons must be followed by a space or a newline!")
     schema = yaml.load(schemaText)
-  return schema
+    return schema
 
 def GetTemplatingDictFromSchemaFilename(fn):
-  obj = LoadSchema(fn)
-  genOrder = ComputeRequiredDeclarationOrder(obj)
-  templatingDict = ProcessSchema(obj, genOrder)
+  return GetTemplatingDictFromSchema(LoadSchema(fn))
+
+def GetTemplatingDictFromSchema(schema):
+  genOrder = ComputeRequiredDeclarationOrder(schema)
+  templatingDict = ProcessSchema(schema, genOrder)
   currentDT = datetime.datetime.now()
   templatingDict['currentDatetime'] = str(currentDT)
   return templatingDict
--- a/Resources/CodeGeneration/stonegentool_test.py	Tue May 14 16:54:13 2019 +0200
+++ b/Resources/CodeGeneration/stonegentool_test.py	Tue May 14 18:39:50 2019 +0200
@@ -6,7 +6,7 @@
 from stonegentool import \
 EatToken,SplitListOfTypes,ParseTemplateType,ProcessSchema, \
 CheckSchemaSchema,LoadSchema,trim,ComputeRequiredDeclarationOrder, \
-GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile
+GetTemplatingDictFromSchemaFilename,MakeTemplate,MakeTemplateFromFile,LoadSchemaFromString,GetTemplatingDictFromSchema
 import unittest
 import os
 import re
@@ -127,17 +127,10 @@
     self.assertTrue('someBs' in structs['C']['fields'])
     self.assertTrue('CrispType' in enums)
     self.assertTrue('Message1' in structs)
-    message1Struct = structs['Message1']
-    self.assertDictEqual(message1Struct,
-    {
-      'name':'Message1',
-      'fields': {
-        'a': 'int32',
-        'b': 'string',
-        'c': 'EnumMonth0',
-        'd': 'bool'
-      }
-    })
+    self.assertEqual('int32', structs['Message1']['fields']['a'].type)
+    self.assertEqual('string', structs['Message1']['fields']['b'].type)
+    self.assertEqual('EnumMonth0', structs['Message1']['fields']['c'].type)
+    self.assertEqual('bool', structs['Message1']['fields']['d'].type)
 
   def test_GenerateTypeScriptEnums(self):
     fn = os.path.join(os.path.dirname(__file__), 'test_data', 'test1.yaml')
@@ -164,6 +157,12 @@
     Barbecue,
   };
 
+  export enum EnumMonth0 {
+    January,
+    February,
+    March,
+  };
+
 """
     self.assertEqual(renderedCodeRef,renderedCode)
 
@@ -192,6 +191,12 @@
     Barbecue,
   };
 
+  enum EnumMonth0 {
+    January,
+    February,
+    March,
+  };
+
 """
     self.assertEqual(renderedCodeRef,renderedCode)
 
@@ -255,10 +260,10 @@
 #     }  };""")
     template = MakeTemplate("""  // end of generic methods
 {% for struct in structs%}  export class {{struct['name']}} {
-{% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key])}};
+{% for key in struct['fields']%}    {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
 {% endfor %}
     constructor() {
-{% for key in struct['fields']%}      this.{{key}} = new {{CanonToTs(struct['fields'][key])}}();
+{% for key in struct['fields']%}      this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
 {% endfor %}    }
 
     public StoneSerialize(): string {
@@ -278,9 +283,9 @@
     movies:Array<MovieType>;
 
     constructor() {
-      someStrings = new Array<string>();
-      someInts2 = new Array<number>();
-      movies = new Array<MovieType>();
+      this.someStrings = new Array<string>();
+      this.someInts2 = new Array<number>();
+      this.movies = new Array<MovieType>();
     }
 
     public StoneSerialize(): string {
@@ -296,8 +301,8 @@
     someInts:Array<number>;
 
     constructor() {
-      someAs = new Array<A>();
-      someInts = new Array<number>();
+      this.someAs = new Array<A>();
+      this.someInts = new Array<number>();
     }
 
     public StoneSerialize(): string {
@@ -313,8 +318,8 @@
     ddd:Array<string>;
 
     constructor() {
-      someBs = new Array<B>();
-      ddd = new Array<string>();
+      this.someBs = new Array<B>();
+      this.ddd = new Array<string>();
     }
 
     public StoneSerialize(): string {
@@ -332,10 +337,10 @@
     d:boolean;
 
     constructor() {
-      a = new number();
-      b = new string();
-      c = new EnumMonth0();
-      d = new boolean();
+      this.a = new number();
+      this.b = new string();
+      this.c = new EnumMonth0();
+      this.d = new boolean();
     }
 
     public StoneSerialize(): string {
@@ -352,13 +357,15 @@
     tutu:Array<string>;
     titi:Map<string, string>;
     lulu:Map<string, Message1>;
+    movieType:MovieType;
 
     constructor() {
-      toto = new string();
-      tata = new Array<Message1>();
-      tutu = new Array<string>();
-      titi = new Map<string, string>();
-      lulu = new Map<string, Message1>();
+      this.toto = new string();
+      this.tata = new Array<Message1>();
+      this.tutu = new Array<string>();
+      this.titi = new Map<string, string>();
+      this.lulu = new Map<string, Message1>();
+      this.movieType = new MovieType();
     }
 
     public StoneSerialize(): string {
@@ -396,6 +403,53 @@
   def test_GenerateCppDispatcher(self):
     pass
 
+  def test_StringDefaultValueInTs(self):
+    schema = LoadSchemaFromString("""
+                                  rootName: MyTest
+                                  struct Toto:
+                                    withoutDefault: string
+                                    withDefault: string = \"tutu\"
+                                  """)
+    tdico = GetTemplatingDictFromSchema(schema)
+
+    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    self.assertIn("withDefault = \"tutu\"", renderedCode)
+    # print(renderedCode)
+
+    cppTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
+    template = MakeTemplateFromFile(cppTemplateFile)
+    renderedCode = template.render(**tdico)
+    print(renderedCode)
+    self.assertIn("withDefault = \"tutu\"", renderedCode)
+
+
+  def test_EnumDefaultValue(self):
+    schema = LoadSchemaFromString("""
+                                  rootName: MyTest
+                                  enum MyEnum:
+                                    - Toto
+                                    - Tutu
+                                  struct Toto:
+                                    withoutDefault: MyEnum
+                                    withDefault: MyEnum = Toto
+                                  """)
+    tdico = GetTemplatingDictFromSchema(schema)
+
+    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.ts.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    # print(renderedCode)
+    self.assertIn("withDefault = MyEnum.Toto", renderedCode)
+
+    tsTemplateFile = os.path.join(os.path.dirname(__file__), 'template.in.h.j2')
+    template = MakeTemplateFromFile(tsTemplateFile)
+    renderedCode = template.render(**tdico)
+    self.assertIn("withDefault = MyTest::MyEnum_Toto", renderedCode)
+    # print(renderedCode)
+
+
 # def test(self):
 #   s = 'hello world'
 #   self.assertEqual(s.split(), ['hello', 'world'])
--- a/Resources/CodeGeneration/template.in.h.j2	Tue May 14 16:54:13 2019 +0200
+++ b/Resources/CodeGeneration/template.in.h.j2	Tue May 14 18:39:50 2019 +0200
@@ -29,7 +29,10 @@
   /** Throws in case of problem */
   inline void _StoneDeserializeValue(int32_t& destValue, const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asInt();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asInt();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(int32_t value)
@@ -40,7 +43,10 @@
 
   inline void _StoneDeserializeValue(int64_t& destValue, const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asInt64();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asInt64();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(int64_t value)
@@ -51,7 +57,10 @@
 
   inline void _StoneDeserializeValue(uint32_t& destValue, const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asUInt();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asUInt();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(uint32_t value)
@@ -62,7 +71,10 @@
 
   inline void _StoneDeserializeValue(uint64_t& destValue, const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asUInt64();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asUInt64();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(uint64_t value)
@@ -84,7 +96,10 @@
   /** Throws in case of problem */
   inline void _StoneDeserializeValue(double& destValue, const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asDouble();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asDouble();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(double value)
@@ -96,7 +111,10 @@
   /** Throws in case of problem */
   inline void _StoneDeserializeValue(bool& destValue, const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asBool();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asBool();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(bool value)
@@ -110,7 +128,10 @@
        std::string& destValue
      , const Json::Value& jsonValue)
   {
-    destValue = jsonValue.asString();
+    if (!jsonValue.isNull())
+    {
+      destValue = jsonValue.asString();
+    }
   }
 
   inline Json::Value _StoneSerializeValue(const std::string& value)
@@ -151,19 +172,22 @@
   void _StoneDeserializeValue(
     std::map<std::string, T>& destValue, const Json::Value& jsonValue)
   {
-    destValue.clear();
-    for (
-      Json::Value::const_iterator itr = jsonValue.begin();
-      itr != jsonValue.end();
-      itr++)
+    if (!jsonValue.isNull())
     {
-      std::string key;
-      _StoneDeserializeValue(key, itr.key());
+      destValue.clear();
+      for (
+        Json::Value::const_iterator itr = jsonValue.begin();
+        itr != jsonValue.end();
+        itr++)
+      {
+        std::string key;
+        _StoneDeserializeValue(key, itr.key());
 
-      T innerDestValue;
-      _StoneDeserializeValue(innerDestValue, *itr);
+        T innerDestValue;
+        _StoneDeserializeValue(innerDestValue, *itr);
 
-      destValue[key] = innerDestValue;
+        destValue[key] = innerDestValue;
+      }
     }
   }
 
@@ -200,13 +224,16 @@
   void _StoneDeserializeValue(
     std::vector<T>& destValue, const Json::Value& jsonValue)
   {
-    destValue.clear();
-    destValue.reserve(jsonValue.size());
-    for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+    if (!jsonValue.isNull())
     {
-      T innerDestValue;
-      _StoneDeserializeValue(innerDestValue, jsonValue[i]);
-      destValue.push_back(innerDestValue);
+      destValue.clear();
+      destValue.reserve(jsonValue.size());
+      for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+      {
+        T innerDestValue;
+        _StoneDeserializeValue(innerDestValue, jsonValue[i]);
+        destValue.push_back(innerDestValue);
+      }
     }
   }
 
@@ -238,12 +265,15 @@
   void _StoneDeserializeValue(
     std::set<T>& destValue, const Json::Value& jsonValue)
   {
-    destValue.clear();
-    for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+    if (!jsonValue.isNull())
     {
-      T innerDestValue;
-      _StoneDeserializeValue(innerDestValue, jsonValue[i]);
-      destValue.insert(innerDestValue);
+      destValue.clear();
+      for (Json::Value::ArrayIndex i = 0; i != jsonValue.size(); i++)
+      {
+        T innerDestValue;
+        _StoneDeserializeValue(innerDestValue, jsonValue[i]);
+        destValue.insert(innerDestValue);
+      }
     }
   }
 
@@ -335,7 +365,10 @@
   inline void _StoneDeserializeValue(
     {{enum['name']}}& destValue, const Json::Value& jsonValue)
   {
-    FromString(destValue, jsonValue.asString());
+    if (!jsonValue.isNull())
+    {
+      FromString(destValue, jsonValue.asString());
+    }
   }
 
   inline Json::Value _StoneSerializeValue(const {{enum['name']}}& value)
@@ -361,9 +394,9 @@
 
   struct {{struct['name']}}
   {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    {{CanonToCpp(struct['fields'][key])}} {{key}};
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    {{CanonToCpp(struct['fields'][key]['type'])}} {{key}};
 {% endfor %}{% endif %}{% endif %}
-    {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{{CanonToCpp(struct['fields'][key])}} {{key}} = {{CanonToCpp(struct['fields'][key])}}(){{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %})
+    {{struct['name']}}({% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{{CanonToCpp(struct['fields'][key]['type'])}} {{key}} = {% if struct['fields'][key]['defaultValue'] %}{{DefaultValueToCpp(rootName,enums,struct['fields'][key])}} {%else%} {{CanonToCpp(struct['fields'][key]['type'])}}() {%endif%} {{ ", " if not loop.last }}{% endfor %}{% endif %}{% endif %})
     {
 {% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      this->{{key}} = {{key}};
 {% endfor %}{% endif %}{% endif %}    }
@@ -371,8 +404,11 @@
 
   inline void _StoneDeserializeValue({{struct['name']}}& destValue, const Json::Value& value)
   {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}    _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]);
+    if (!value.isNull())
+    {
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}      _StoneDeserializeValue(destValue.{{key}}, value["{{key}}"]);
 {% endfor %}{% endif %}{% endif %}    }
+  }
 
   inline Json::Value _StoneSerializeValue(const {{struct['name']}}& value)
   {
--- a/Resources/CodeGeneration/template.in.ts.j2	Tue May 14 16:54:13 2019 +0200
+++ b/Resources/CodeGeneration/template.in.ts.j2	Tue May 14 18:39:50 2019 +0200
@@ -72,10 +72,12 @@
 
 
 {% for struct in structs%}export class {{struct['name']}} {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}  {{key}}:{{CanonToTs(struct['fields'][key])}};
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}  {{key}}:{{CanonToTs(struct['fields'][key]['type'])}};
 {% endfor %}{% endif %}{% endif %}
   constructor() {
-{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key])) %}    this.{{key}} = new {{CanonToTs(struct['fields'][key])}}();
+{% if struct %}{% if struct['fields'] %}{% for key in struct['fields']%}{% if NeedsTsConstruction(enums,CanonToTs(struct['fields'][key]['type'])) %}    this.{{key}} = new {{CanonToTs(struct['fields'][key]['type'])}}();
+{% endif %}
+{% if struct['fields'][key]['defaultValue'] %}    this.{{key}} = {{DefaultValueToTs(enums,struct['fields'][key])}};
 {% endif %}{% endfor %}{% endif %}{% endif %}  }
 
   public StoneSerialize(): string {
--- a/Resources/CodeGeneration/test_data/test1.yaml	Tue May 14 16:54:13 2019 +0200
+++ b/Resources/CodeGeneration/test_data/test1.yaml	Tue May 14 18:39:50 2019 +0200
@@ -24,7 +24,7 @@
   d: bool
 
 struct Message2:
-  toto: string
+  toto: string = "my-default-value"
   tata: vector<Message1>
   tutu: vector<string>
   titi: map<string, string>