Add feature to store original JSON string for extras and extensions(for user-specific JSON parsing). # Fixes 215
Fix some glTF object(e.g. Skin) does not have extras and/or extensions property.
diff --git a/loader_example.cc b/loader_example.cc
index 93a6866..dbe10ad 100644
--- a/loader_example.cc
+++ b/loader_example.cc
@@ -322,6 +322,15 @@
   }
 }
 
+static void DumpExtensions(const tinygltf::ExtensionMap &extension,
+                           const int indent) {
+  // TODO(syoyo): pritty print Value
+  for (auto &e : extension) {
+    std::cout << Indent(indent) << e.first << std::endl;
+    std::cout << PrintValue("extensions", e.second, indent + 1) << std::endl;
+  }
+}
+
 static void DumpPrimitive(const tinygltf::Primitive &primitive, int indent) {
   std::cout << Indent(indent) << "material : " << primitive.material
             << std::endl;
@@ -333,19 +342,23 @@
             << std::endl;
   DumpStringIntMap(primitive.attributes, indent + 1);
 
+  DumpExtensions(primitive.extensions, indent);
   std::cout << Indent(indent) << "extras :" << std::endl
             << PrintValue("extras", primitive.extras, indent + 1) << std::endl;
-}
 
-static void DumpExtensions(const tinygltf::ExtensionMap &extension,
-                           const int indent) {
-  // TODO(syoyo): pritty print Value
-  for (auto &e : extension) {
-    std::cout << Indent(indent) << e.first << std::endl;
-    std::cout << PrintValue("extensions", e.second, indent + 1) << std::endl;
+  if (!primitive.extensions_json_string.empty()) {
+    std::cout << Indent(indent + 1) << "extensions(JSON string) = "
+              << primitive.extensions_json_string << "\n";
+  }
+
+  if (!primitive.extras_json_string.empty()) {
+    std::cout << Indent(indent + 1)
+              << "extras(JSON string) = " << primitive.extras_json_string
+              << "\n";
   }
 }
 
+
 static void DumpTextureInfo(const tinygltf::TextureInfo &texinfo,
                             const int indent) {
   std::cout << Indent(indent) << "index     : " << texinfo.index << "\n";
@@ -353,6 +366,17 @@
             << "\n";
   DumpExtensions(texinfo.extensions, indent + 1);
   std::cout << PrintValue("extras", texinfo.extras, indent + 1) << "\n";
+
+  if (!texinfo.extensions_json_string.empty()) {
+    std::cout << Indent(indent)
+              << "extensions(JSON string) = " << texinfo.extensions_json_string
+              << "\n";
+  }
+
+  if (!texinfo.extras_json_string.empty()) {
+    std::cout << Indent(indent)
+              << "extras(JSON string) = " << texinfo.extras_json_string << "\n";
+  }
 }
 
 static void DumpNormalTextureInfo(const tinygltf::NormalTextureInfo &texinfo,
@@ -546,6 +570,21 @@
       std::cout << Indent(2)
                 << "target       : " << PrintTarget(bufferView.target)
                 << std::endl;
+      std::cout << Indent(1) << "-------------------------------------\n";
+
+      DumpExtensions(bufferView.extensions, 1);
+      std::cout << PrintValue("extras", bufferView.extras, 2) << std::endl;
+
+      if (!bufferView.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << bufferView.extensions_json_string << "\n";
+      }
+
+      if (!bufferView.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << bufferView.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -556,6 +595,21 @@
       std::cout << Indent(1) << "name         : " << buffer.name << std::endl;
       std::cout << Indent(2) << "byteLength   : " << buffer.data.size()
                 << std::endl;
+      std::cout << Indent(1) << "-------------------------------------\n";
+
+      DumpExtensions(buffer.extensions, 1);
+      std::cout << PrintValue("extras", buffer.extras, 2) << std::endl;
+
+      if (!buffer.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << buffer.extensions_json_string << "\n";
+      }
+
+      if (!buffer.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << buffer.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -602,6 +656,17 @@
 
       DumpExtensions(material.extensions, 1);
       std::cout << PrintValue("extras", material.extras, 2) << std::endl;
+
+      if (!material.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << material.extensions_json_string << "\n";
+      }
+
+      if (!material.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << material.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -625,6 +690,18 @@
       std::cout << Indent(2) << "height    : " << image.height << std::endl;
       std::cout << Indent(2) << "component : " << image.component << std::endl;
       DumpExtensions(image.extensions, 1);
+      std::cout << PrintValue("extras", image.extras, 2) << std::endl;
+
+      if (!image.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << image.extensions_json_string << "\n";
+      }
+
+      if (!image.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << image.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -637,6 +714,18 @@
       std::cout << Indent(1) << "source         : " << texture.source
                 << std::endl;
       DumpExtensions(texture.extensions, 1);
+      std::cout << PrintValue("extras", texture.extras, 2) << std::endl;
+
+      if (!texture.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << texture.extensions_json_string << "\n";
+      }
+
+      if (!texture.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << texture.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -658,6 +747,20 @@
       std::cout << Indent(2)
                 << "wrapT        : " << PrintWrapMode(sampler.wrapT)
                 << std::endl;
+
+      DumpExtensions(sampler.extensions, 1);
+      std::cout << PrintValue("extras", sampler.extras, 2) << std::endl;
+
+      if (!sampler.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << sampler.extensions_json_string << "\n";
+      }
+
+      if (!sampler.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << sampler.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -690,6 +793,54 @@
                   << "znear         : " << camera.orthographic.znear
                   << std::endl;
       }
+
+      std::cout << Indent(1) << "-------------------------------------\n";
+
+      DumpExtensions(camera.extensions, 1);
+      std::cout << PrintValue("extras", camera.extras, 2) << std::endl;
+
+      if (!camera.extensions_json_string.empty()) {
+        std::cout << Indent(2) << "extensions(JSON string) = "
+                  << camera.extensions_json_string << "\n";
+      }
+
+      if (!camera.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << camera.extras_json_string
+                  << "\n";
+      }
+    }
+  }
+
+  {
+    std::cout << "skins(items=" << model.skins.size() << ")" << std::endl;
+    for (size_t i = 0; i < model.skins.size(); i++) {
+      const tinygltf::Skin &skin = model.skins[i];
+      std::cout << Indent(1) << "name         : " << skin.name << std::endl;
+      std::cout << Indent(2)
+                << "inverseBindMatrices   : " << skin.inverseBindMatrices
+                << std::endl;
+      std::cout << Indent(2) << "skeleton              : " << skin.skeleton
+                << std::endl;
+      std::cout << Indent(2)
+                << "joints                : " << PrintIntArray(skin.joints)
+                << std::endl;
+      std::cout << Indent(1) << "-------------------------------------\n";
+
+      DumpExtensions(skin.extensions, 1);
+      std::cout << PrintValue("extras", skin.extras, 2) << std::endl;
+
+      if (!skin.extensions_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extensions(JSON string) = " << skin.extensions_json_string
+                  << "\n";
+      }
+
+      if (!skin.extras_json_string.empty()) {
+        std::cout << Indent(2)
+                  << "extras(JSON string) = " << skin.extras_json_string
+                  << "\n";
+      }
     }
   }
 
@@ -707,6 +858,12 @@
     exit(1);
   }
 
+  // Store original JSON string for `extras` and `extensions`
+  bool store_original_json_for_extras_and_extensions = false;
+  if (argc > 2) {
+    store_original_json_for_extras_and_extensions = true;
+  }
+
   tinygltf::Model model;
   tinygltf::TinyGLTF gltf_ctx;
   std::string err;
@@ -714,6 +871,9 @@
   std::string input_filename(argv[1]);
   std::string ext = GetFilePathExtension(input_filename);
 
+  gltf_ctx.SetStoreOriginalJSONForExtrasAndExtensions(
+      store_original_json_for_extras_and_extensions);
+
   bool ret = false;
   if (ext.compare("glb") == 0) {
     std::cout << "Reading binary glTF" << std::endl;
diff --git a/tiny_gltf.h b/tiny_gltf.h
index 23b16c9..dea02e7 100644
--- a/tiny_gltf.h
+++ b/tiny_gltf.h
@@ -26,6 +26,8 @@
 // THE SOFTWARE.
 
 // Version:
+//  - v2.4.1 Fix some glTF object class does not have `extensions` and/or
+//  `extras` property.
 //  - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
 //  - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
 //  - v2.3.0 Modified Material representation according to glTF 2.0 schema
@@ -77,12 +79,12 @@
 #define TINYGLTF_NOEXCEPT noexcept
 #endif
 
-#define DEFAULT_METHODS(x)								\
-	~x() = default;										\
-	x(const x&) = default;								\
-	x(x&&) TINYGLTF_NOEXCEPT = default;					\
-	x& operator=(const x&) = default;					\
-	x& operator=(x&&) TINYGLTF_NOEXCEPT = default;
+#define DEFAULT_METHODS(x)             \
+  ~x() = default;                      \
+  x(const x &) = default;              \
+  x(x &&) TINYGLTF_NOEXCEPT = default; \
+  x &operator=(const x &) = default;   \
+  x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
 
 namespace tinygltf {
 
@@ -525,6 +527,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   AnimationChannel() : sampler(-1), target_node(-1) {}
   DEFAULT_METHODS(AnimationChannel)
   bool operator==(const AnimationChannel &) const;
@@ -536,6 +542,11 @@
   std::string interpolation;  // "LINEAR", "STEP","CUBICSPLINE" or user defined
                               // string. default "LINEAR"
   Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 
   AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
   DEFAULT_METHODS(AnimationSampler)
@@ -549,6 +560,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Animation() = default;
   DEFAULT_METHODS(Animation)
   bool operator==(const Animation &) const;
@@ -560,6 +575,13 @@
   int skeleton;             // The index of the node used as a skeleton root
   std::vector<int> joints;  // Indices of skeleton nodes
 
+  Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Skin() {
     inverseBindMatrices = -1;
     skeleton = -1;
@@ -585,7 +607,13 @@
       TINYGLTF_TEXTURE_WRAP_REPEAT;  // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
                                      // "REPEAT"], default "REPEAT"
   int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;  // TinyGLTF extension
+
   Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 
   Sampler()
       : minFilter(-1),
@@ -613,6 +641,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
   // compressed for "image/jpeg" mime) This feature is good if you use custom
   // image loader function. (e.g. delayed decoding of images for faster glTF
@@ -640,6 +672,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Texture() : sampler(-1), source(-1) {}
   DEFAULT_METHODS(Texture)
 
@@ -654,6 +690,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   TextureInfo() : index(-1), texCoord(0) {}
   DEFAULT_METHODS(TextureInfo)
   bool operator==(const TextureInfo &) const;
@@ -669,6 +709,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
   DEFAULT_METHODS(NormalTextureInfo)
   bool operator==(const NormalTextureInfo &) const;
@@ -684,6 +728,10 @@
   Value extras;
   ExtensionMap extensions;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
   DEFAULT_METHODS(OcclusionTextureInfo)
   bool operator==(const OcclusionTextureInfo &) const;
@@ -700,7 +748,14 @@
   Value extras;
   ExtensionMap extensions;
 
-  PbrMetallicRoughness() : baseColorFactor(std::vector<double>{ 1.0,1.0,1.0,1.0 }), metallicFactor(1.0), roughnessFactor(1.0) {}
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
+  PbrMetallicRoughness()
+      : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
+        metallicFactor(1.0),
+        roughnessFactor(1.0) {}
   DEFAULT_METHODS(PbrMetallicRoughness)
   bool operator==(const PbrMetallicRoughness &) const;
 };
@@ -730,6 +785,10 @@
   ExtensionMap extensions;
   Value extras;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
   DEFAULT_METHODS(Material)
 
@@ -745,6 +804,12 @@
                       // understood to be tightly packed
   int target;         // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"]
   Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   bool dracoDecoded;  // Flag indicating this has been draco decoded
 
   BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {}
@@ -762,6 +827,11 @@
   size_t count;       // required
   int type;           // (required) One of TINYGLTF_TYPE_***   ..
   Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 
   std::vector<double> minValues;  // optional
   std::vector<double> maxValues;  // optional
@@ -842,6 +912,10 @@
 
   ExtensionMap extensions;
   Value extras;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 struct OrthographicCamera {
@@ -856,6 +930,10 @@
 
   ExtensionMap extensions;
   Value extras;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 struct Camera {
@@ -871,6 +949,10 @@
 
   ExtensionMap extensions;
   Value extras;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 struct Primitive {
@@ -889,6 +971,10 @@
   ExtensionMap extensions;
   Value extras;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Primitive() {
     material = -1;
     indices = -1;
@@ -904,6 +990,10 @@
   ExtensionMap extensions;
   Value extras;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Mesh() = default;
   DEFAULT_METHODS(Mesh)
   bool operator==(const Mesh &) const;
@@ -931,6 +1021,10 @@
 
   ExtensionMap extensions;
   Value extras;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 struct Buffer {
@@ -939,6 +1033,11 @@
   std::string
       uri;  // considered as required here but not in the spec (need to clarify)
   Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 
   Buffer() = default;
   DEFAULT_METHODS(Buffer)
@@ -953,6 +1052,10 @@
   ExtensionMap extensions;
   Value extras;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Asset() = default;
   DEFAULT_METHODS(Asset)
   bool operator==(const Asset &) const;
@@ -965,6 +1068,10 @@
   ExtensionMap extensions;
   Value extras;
 
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
+
   Scene() = default;
   DEFAULT_METHODS(Scene)
   bool operator==(const Scene &) const;
@@ -980,6 +1087,10 @@
 
   ExtensionMap extensions;
   Value extras;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 struct Light {
@@ -997,6 +1108,10 @@
 
   ExtensionMap extensions;
   Value extras;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 class Model {
@@ -1020,7 +1135,6 @@
   std::vector<Camera> cameras;
   std::vector<Scene> scenes;
   std::vector<Light> lights;
-  ExtensionMap extensions;
 
   int defaultScene;
   std::vector<std::string> extensionsUsed;
@@ -1029,6 +1143,11 @@
   Asset asset;
 
   Value extras;
+  ExtensionMap extensions;
+
+  // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
+  std::string extras_json_string;
+  std::string extensions_json_string;
 };
 
 enum SectionCheck {
@@ -1222,6 +1341,19 @@
 
   bool GetSerializeDefaultValues() const { return serialize_default_values_; }
 
+  ///
+  /// Store original JSON string for `extras` and `extensions`.
+  /// This feature will be useful when the user want to reconstruct custom data
+  /// structure from JSON string.
+  ///
+  void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
+    store_original_json_for_extras_and_extensions_ = enabled;
+  }
+
+  bool GetStoreOriginalJSONForExtrasAndExtensions() const {
+    return store_original_json_for_extras_and_extensions_;
+  }
+
  private:
   ///
   /// Loads glTF asset from string(memory).
@@ -1239,6 +1371,8 @@
 
   bool serialize_default_values_ = false;  ///< Serialize default values?
 
+  bool store_original_json_for_extras_and_extensions_ = false;
+
   FsCallbacks fs = {
 #ifndef TINYGLTF_NO_FS
       &tinygltf::FileExists, &tinygltf::ExpandFilePath,
@@ -1293,7 +1427,9 @@
 #pragma clang diagnostic ignored "-Wconversion"
 #pragma clang diagnostic ignored "-Wold-style-cast"
 #pragma clang diagnostic ignored "-Wglobal-constructors"
+#if __has_warning("-Wreserved-id-macro")
 #pragma clang diagnostic ignored "-Wreserved-id-macro"
+#endif
 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
 #pragma clang diagnostic ignored "-Wpadded"
 #pragma clang diagnostic ignored "-Wc++98-compat"
@@ -1443,6 +1579,13 @@
   assert(s_pActiveDocument);  // Root json node must be JsonDocument type
   return s_pActiveDocument->GetAllocator();
 }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+// Suppress JsonDocument(JsonDocument &&rhs) noexcept
+#pragma clang diagnostic ignored "-Wunused-member-function"
+#endif
+
 struct JsonDocument : public rapidjson::Document {
   JsonDocument() {
     assert(s_pActiveDocument ==
@@ -1466,7 +1609,13 @@
  private:
   bool isNil = false;
 };
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
 #endif  // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
+
 #else
 using nlohmann::json;
 using json_const_iterator = json::const_iterator;
@@ -1477,6 +1626,7 @@
 void JsonParse(JsonDocument &doc, const char *str, size_t length,
                bool throwExc = false) {
 #ifdef TINYGLTF_USE_RAPIDJSON
+  (void)throwExc;
   doc.Parse(str, length);
 #else
   doc = json::parse(str, str + length, nullptr, throwExc);
@@ -1552,7 +1702,8 @@
   return this->bufferView == other.bufferView &&
          this->byteOffset == other.byteOffset &&
          this->componentType == other.componentType &&
-         this->count == other.count && this->extras == other.extras &&
+         this->count == other.count && this->extensions == other.extensions &&
+         this->extras == other.extras &&
          Equals(this->maxValues, other.maxValues) &&
          Equals(this->minValues, other.minValues) && this->name == other.name &&
          this->normalized == other.normalized && this->type == other.type;
@@ -1569,7 +1720,8 @@
          this->sampler == other.sampler;
 }
 bool AnimationSampler::operator==(const AnimationSampler &other) const {
-  return this->extras == other.extras && this->input == other.input &&
+  return this->extras == other.extras && this->extensions == other.extensions &&
+         this->input == other.input &&
          this->interpolation == other.interpolation &&
          this->output == other.output;
 }
@@ -1580,14 +1732,16 @@
          this->minVersion == other.minVersion && this->version == other.version;
 }
 bool Buffer::operator==(const Buffer &other) const {
-  return this->data == other.data && this->extras == other.extras &&
-         this->name == other.name && this->uri == other.uri;
+  return this->data == other.data && this->extensions == other.extensions &&
+         this->extras == other.extras && this->name == other.name &&
+         this->uri == other.uri;
 }
 bool BufferView::operator==(const BufferView &other) const {
   return this->buffer == other.buffer && this->byteLength == other.byteLength &&
          this->byteOffset == other.byteOffset &&
          this->byteStride == other.byteStride && this->name == other.name &&
-         this->target == other.target && this->extras == other.extras &&
+         this->target == other.target && this->extensions == other.extensions &&
+         this->extras == other.extras &&
          this->dracoDecoded == other.dracoDecoded;
 }
 bool Camera::operator==(const Camera &other) const {
@@ -1598,7 +1752,8 @@
 }
 bool Image::operator==(const Image &other) const {
   return this->bufferView == other.bufferView &&
-         this->component == other.component && this->extras == other.extras &&
+         this->component == other.component &&
+         this->extensions == other.extensions && this->extras == other.extras &&
          this->height == other.height && this->image == other.image &&
          this->mimeType == other.mimeType && this->name == other.name &&
          this->uri == other.uri && this->width == other.width;
@@ -1699,7 +1854,8 @@
          this->mode == other.mode && this->targets == other.targets;
 }
 bool Sampler::operator==(const Sampler &other) const {
-  return this->extras == other.extras && this->magFilter == other.magFilter &&
+  return this->extensions == other.extensions && this->extras == other.extras &&
+         this->magFilter == other.magFilter &&
          this->minFilter == other.minFilter && this->name == other.name &&
          this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
          this->wrapT == other.wrapT;
@@ -1709,7 +1865,8 @@
          this->name == other.name && this->nodes == other.nodes;
 }
 bool Skin::operator==(const Skin &other) const {
-  return this->inverseBindMatrices == other.inverseBindMatrices &&
+  return this->extensions == other.extensions && this->extras == other.extras &&
+         this->inverseBindMatrices == other.inverseBindMatrices &&
          this->joints == other.joints && this->name == other.name &&
          this->skeleton == other.skeleton;
 }
@@ -2690,6 +2847,25 @@
   return it.value();
 #endif
 }
+
+std::string JsonToString(const json &o, int spacing = -1) {
+#ifdef TINYGLTF_USE_RAPIDJSON
+  using namespace rapidjson;
+  StringBuffer buffer;
+  if (spacing == -1) {
+    Writer<StringBuffer> writer(buffer);
+    o.Accept(writer);
+  } else {
+    PrettyWriter<StringBuffer> writer(buffer);
+    writer.SetIndent(' ', uint32_t(spacing));
+    o.Accept(writer);
+  }
+  return buffer.GetString();
+#else
+  return o.dump(spacing);
+#endif
+}
+
 }  // namespace
 
 static bool ParseJsonAsValue(Value *ret, const json &o) {
@@ -2737,9 +2913,8 @@
       }
       break;
     case Type::kNullType:
-    default:
-      // default:
       break;
+      // all types are covered, so no `case default`
   }
 #else
   switch (o.type()) {
@@ -3275,7 +3450,8 @@
   return true;
 }
 
-static bool ParseAsset(Asset *asset, std::string *err, const json &o) {
+static bool ParseAsset(Asset *asset, std::string *err, const json &o,
+                       bool store_original_json_for_extras_and_extensions) {
   ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
   ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
   ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
@@ -3285,11 +3461,27 @@
   // Unity exporter version is added as extra here
   ParseExtrasProperty(&(asset->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        asset->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        asset->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
 static bool ParseImage(Image *image, const int image_idx, std::string *err,
                        std::string *warn, const json &o,
+                       bool store_original_json_for_extras_and_extensions,
                        const std::string &basedir, FsCallbacks *fs,
                        LoadImageDataFunction *LoadImageData = nullptr,
                        void *load_image_user_data = nullptr) {
@@ -3326,6 +3518,21 @@
   ParseExtensionsProperty(&image->extensions, err, o);
   ParseExtrasProperty(&image->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator eit;
+      if (FindMember(o, "extensions", eit)) {
+        image->extensions_json_string = JsonToString(GetValue(eit));
+      }
+    }
+    {
+      json_const_iterator eit;
+      if (FindMember(o, "extras", eit)) {
+        image->extras_json_string = JsonToString(GetValue(eit));
+      }
+    }
+  }
+
   if (hasBufferView) {
     int bufferView = -1;
     if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
@@ -3417,6 +3624,7 @@
 }
 
 static bool ParseTexture(Texture *texture, std::string *err, const json &o,
+                         bool store_original_json_for_extras_and_extensions,
                          const std::string &basedir) {
   (void)basedir;
   int sampler = -1;
@@ -3431,13 +3639,29 @@
   ParseExtensionsProperty(&texture->extensions, err, o);
   ParseExtrasProperty(&texture->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        texture->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        texture->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   ParseStringProperty(&texture->name, err, o, "name", false);
 
   return true;
 }
 
-static bool ParseTextureInfo(TextureInfo *texinfo, std::string *err,
-                             const json &o) {
+static bool ParseTextureInfo(
+    TextureInfo *texinfo, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   if (texinfo == nullptr) {
     return false;
   }
@@ -3452,11 +3676,27 @@
   ParseExtensionsProperty(&texinfo->extensions, err, o);
   ParseExtrasProperty(&texinfo->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        texinfo->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        texinfo->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseNormalTextureInfo(NormalTextureInfo *texinfo, std::string *err,
-                                   const json &o) {
+static bool ParseNormalTextureInfo(
+    NormalTextureInfo *texinfo, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   if (texinfo == nullptr) {
     return false;
   }
@@ -3472,11 +3712,27 @@
   ParseExtensionsProperty(&texinfo->extensions, err, o);
   ParseExtrasProperty(&texinfo->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        texinfo->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        texinfo->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseOcclusionTextureInfo(OcclusionTextureInfo *texinfo,
-                                      std::string *err, const json &o) {
+static bool ParseOcclusionTextureInfo(
+    OcclusionTextureInfo *texinfo, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   if (texinfo == nullptr) {
     return false;
   }
@@ -3492,10 +3748,26 @@
   ParseExtensionsProperty(&texinfo->extensions, err, o);
   ParseExtrasProperty(&texinfo->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        texinfo->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        texinfo->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
 static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
+                        bool store_original_json_for_extras_and_extensions,
                         FsCallbacks *fs, const std::string &basedir,
                         bool is_binary = false,
                         const unsigned char *bin_data = nullptr,
@@ -3596,11 +3868,30 @@
 
   ParseStringProperty(&buffer->name, err, o, "name", false);
 
+  ParseExtensionsProperty(&buffer->extensions, err, o);
+  ParseExtrasProperty(&buffer->extras, o);
+
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        buffer->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        buffer->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseBufferView(BufferView *bufferView, std::string *err,
-                            const json &o) {
+static bool ParseBufferView(
+    BufferView *bufferView, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   int buffer = -1;
   if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
     return false;
@@ -3649,6 +3940,24 @@
 
   ParseStringProperty(&bufferView->name, err, o, "name", false);
 
+  ParseExtensionsProperty(&bufferView->extensions, err, o);
+  ParseExtrasProperty(&bufferView->extras, o);
+
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        bufferView->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        bufferView->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   bufferView->buffer = buffer;
   bufferView->byteOffset = byteOffset;
   bufferView->byteLength = byteLength;
@@ -3704,7 +4013,8 @@
   return true;
 }
 
-static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) {
+static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
+                          bool store_original_json_for_extras_and_extensions) {
   int bufferView = -1;
   ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
 
@@ -3783,8 +4093,24 @@
     }
   }
 
+  ParseExtensionsProperty(&(accessor->extensions), err, o);
   ParseExtrasProperty(&(accessor->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        accessor->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        accessor->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   // check if accessor has a "sparse" object:
   json_const_iterator iterator;
   if (FindMember(o, "sparse", iterator)) {
@@ -3988,7 +4314,8 @@
 #endif
 
 static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
-                           const json &o) {
+                           const json &o,
+                           bool store_original_json_for_extras_and_extensions) {
   int material = -1;
   ParseIntegerProperty(&material, err, o, "material", false);
   primitive->material = material;
@@ -4030,9 +4357,23 @@
   }
 
   ParseExtrasProperty(&(primitive->extras), o);
-
   ParseExtensionsProperty(&primitive->extensions, err, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        primitive->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        primitive->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
 #ifdef TINYGLTF_ENABLE_DRACO
   auto dracoExtension =
       primitive->extensions.find("KHR_draco_mesh_compression");
@@ -4046,8 +4387,8 @@
   return true;
 }
 
-static bool ParseMesh(Mesh *mesh, Model *model, std::string *err,
-                      const json &o) {
+static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
+                      bool store_original_json_for_extras_and_extensions) {
   ParseStringProperty(&mesh->name, err, o, "name", false);
 
   mesh->primitives.clear();
@@ -4058,7 +4399,8 @@
     for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
          i != primEnd; ++i) {
       Primitive primitive;
-      if (ParsePrimitive(&primitive, model, err, *i)) {
+      if (ParsePrimitive(&primitive, model, err, *i,
+                         store_original_json_for_extras_and_extensions)) {
         // Only add the primitive if the parsing succeeds.
         mesh->primitives.emplace_back(std::move(primitive));
       }
@@ -4071,10 +4413,26 @@
   ParseExtensionsProperty(&mesh->extensions, err, o);
   ParseExtrasProperty(&(mesh->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        mesh->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        mesh->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseNode(Node *node, std::string *err, const json &o) {
+static bool ParseNode(Node *node, std::string *err, const json &o,
+                      bool store_original_json_for_extras_and_extensions) {
   ParseStringProperty(&node->name, err, o, "name", false);
 
   int skin = -1;
@@ -4104,11 +4462,27 @@
   ParseExtensionsProperty(&node->extensions, err, o);
   ParseExtrasProperty(&(node->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        node->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        node->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParsePbrMetallicRoughness(PbrMetallicRoughness *pbr,
-                                      std::string *err, const json &o) {
+static bool ParsePbrMetallicRoughness(
+    PbrMetallicRoughness *pbr, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   if (pbr == nullptr) {
     return false;
   }
@@ -4131,14 +4505,16 @@
   {
     json_const_iterator it;
     if (FindMember(o, "baseColorTexture", it)) {
-      ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it));
+      ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
+                       store_original_json_for_extras_and_extensions);
     }
   }
 
   {
     json_const_iterator it;
     if (FindMember(o, "metallicRoughnessTexture", it)) {
-      ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it));
+      ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
+                       store_original_json_for_extras_and_extensions);
     }
   }
 
@@ -4148,10 +4524,26 @@
   ParseExtensionsProperty(&pbr->extensions, err, o);
   ParseExtrasProperty(&pbr->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        pbr->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        pbr->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseMaterial(Material *material, std::string *err, const json &o) {
+static bool ParseMaterial(Material *material, std::string *err, const json &o,
+                          bool store_original_json_for_extras_and_extensions) {
   ParseStringProperty(&material->name, err, o, "name", /* required */ false);
 
   if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
@@ -4182,28 +4574,32 @@
     json_const_iterator it;
     if (FindMember(o, "pbrMetallicRoughness", it)) {
       ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
-                                GetValue(it));
+                                GetValue(it),
+                                store_original_json_for_extras_and_extensions);
     }
   }
 
   {
     json_const_iterator it;
     if (FindMember(o, "normalTexture", it)) {
-      ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it));
+      ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
+                             store_original_json_for_extras_and_extensions);
     }
   }
 
   {
     json_const_iterator it;
     if (FindMember(o, "occlusionTexture", it)) {
-      ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it));
+      ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
+                                store_original_json_for_extras_and_extensions);
     }
   }
 
   {
     json_const_iterator it;
     if (FindMember(o, "emissiveTexture", it)) {
-      ParseTextureInfo(&material->emissiveTexture, err, GetValue(it));
+      ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
+                       store_original_json_for_extras_and_extensions);
     }
   }
 
@@ -4253,11 +4649,27 @@
   ParseExtensionsProperty(&material->extensions, err, o);
   ParseExtrasProperty(&(material->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator eit;
+      if (FindMember(o, "extensions", eit)) {
+        material->extensions_json_string = JsonToString(GetValue(eit));
+      }
+    }
+    {
+      json_const_iterator eit;
+      if (FindMember(o, "extras", eit)) {
+        material->extras_json_string = JsonToString(GetValue(eit));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err,
-                                  const json &o) {
+static bool ParseAnimationChannel(
+    AnimationChannel *channel, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   int samplerIndex = -1;
   int targetIndex = -1;
   if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
@@ -4294,11 +4706,27 @@
   ParseExtensionsProperty(&channel->extensions, err, o);
   ParseExtrasProperty(&(channel->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        channel->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        channel->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
 static bool ParseAnimation(Animation *animation, std::string *err,
-                           const json &o) {
+                           const json &o,
+                           bool store_original_json_for_extras_and_extensions) {
   {
     json_const_iterator channelsIt;
     if (FindMember(o, "channels", channelsIt) &&
@@ -4307,7 +4735,9 @@
       for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
            i != channelEnd; ++i) {
         AnimationChannel channel;
-        if (ParseAnimationChannel(&channel, err, *i)) {
+        if (ParseAnimationChannel(
+                &channel, err, *i,
+                store_original_json_for_extras_and_extensions)) {
           // Only add the channel if the parsing succeeds.
           animation->channels.emplace_back(std::move(channel));
         }
@@ -4345,7 +4775,24 @@
         }
         sampler.input = inputIndex;
         sampler.output = outputIndex;
+        ParseExtensionsProperty(&(sampler.extensions), err, o);
         ParseExtrasProperty(&(sampler.extras), s);
+
+        if (store_original_json_for_extras_and_extensions) {
+          {
+            json_const_iterator eit;
+            if (FindMember(o, "extensions", eit)) {
+              sampler.extensions_json_string = JsonToString(GetValue(eit));
+            }
+          }
+          {
+            json_const_iterator eit;
+            if (FindMember(o, "extras", eit)) {
+              sampler.extras_json_string = JsonToString(GetValue(eit));
+            }
+          }
+        }
+
         animation->samplers.emplace_back(std::move(sampler));
       }
     }
@@ -4356,10 +4803,26 @@
   ParseExtensionsProperty(&animation->extensions, err, o);
   ParseExtrasProperty(&(animation->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        animation->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        animation->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) {
+static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
+                         bool store_original_json_for_extras_and_extensions) {
   ParseStringProperty(&sampler->name, err, o, "name", false);
 
   int minFilter = -1;
@@ -4382,12 +4845,29 @@
   sampler->wrapT = wrapT;
   sampler->wrapR = wrapR;
 
+  ParseExtensionsProperty(&(sampler->extensions), err, o);
   ParseExtrasProperty(&(sampler->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        sampler->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        sampler->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseSkin(Skin *skin, std::string *err, const json &o) {
+static bool ParseSkin(Skin *skin, std::string *err, const json &o,
+                      bool store_original_json_for_extras_and_extensions) {
   ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
 
   std::vector<int> joints;
@@ -4404,11 +4884,30 @@
   ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
   skin->inverseBindMatrices = invBind;
 
+  ParseExtensionsProperty(&(skin->extensions), err, o);
+  ParseExtrasProperty(&(skin->extras), o);
+
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        skin->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        skin->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err,
-                                   const json &o) {
+static bool ParsePerspectiveCamera(
+    PerspectiveCamera *camera, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   double yfov = 0.0;
   if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
     return false;
@@ -4435,25 +4934,57 @@
   ParseExtensionsProperty(&camera->extensions, err, o);
   ParseExtrasProperty(&(camera->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        camera->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        camera->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   // TODO(syoyo): Validate parameter values.
 
   return true;
 }
 
-static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o) {
+static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
+                           bool store_original_json_for_extras_and_extensions) {
   ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
   ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
 
   ParseExtensionsProperty(&light->extensions, err, o);
   ParseExtrasProperty(&light->extras, o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        light->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        light->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   // TODO(syoyo): Validate parameter values.
 
   return true;
 }
 
-static bool ParseOrthographicCamera(OrthographicCamera *camera,
-                                    std::string *err, const json &o) {
+static bool ParseOrthographicCamera(
+    OrthographicCamera *camera, std::string *err, const json &o,
+    bool store_original_json_for_extras_and_extensions) {
   double xmag = 0.0;
   if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
     return false;
@@ -4478,6 +5009,21 @@
   ParseExtensionsProperty(&camera->extensions, err, o);
   ParseExtrasProperty(&(camera->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        camera->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        camera->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   camera->xmag = xmag;
   camera->ymag = ymag;
   camera->zfar = zfar;
@@ -4488,7 +5034,8 @@
   return true;
 }
 
-static bool ParseCamera(Camera *camera, std::string *err, const json &o) {
+static bool ParseCamera(Camera *camera, std::string *err, const json &o,
+                        bool store_original_json_for_extras_and_extensions) {
   if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
     return false;
   }
@@ -4514,7 +5061,9 @@
       return false;
     }
 
-    if (!ParseOrthographicCamera(&camera->orthographic, err, v)) {
+    if (!ParseOrthographicCamera(
+            &camera->orthographic, err, v,
+            store_original_json_for_extras_and_extensions)) {
       return false;
     }
   } else if (camera->type.compare("perspective") == 0) {
@@ -4538,7 +5087,9 @@
       return false;
     }
 
-    if (!ParsePerspectiveCamera(&camera->perspective, err, v)) {
+    if (!ParsePerspectiveCamera(
+            &camera->perspective, err, v,
+            store_original_json_for_extras_and_extensions)) {
       return false;
     }
   } else {
@@ -4556,10 +5107,26 @@
   ParseExtensionsProperty(&camera->extensions, err, o);
   ParseExtrasProperty(&(camera->extras), o);
 
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        camera->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        camera->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
-static bool ParseLight(Light *light, std::string *err, const json &o) {
+static bool ParseLight(Light *light, std::string *err, const json &o,
+                       bool store_original_json_for_extras_and_extensions) {
   if (!ParseStringProperty(&light->type, err, o, "type", true)) {
     return false;
   }
@@ -4585,7 +5152,8 @@
       return false;
     }
 
-    if (!ParseSpotLight(&light->spot, err, v)) {
+    if (!ParseSpotLight(&light->spot, err, v,
+                        store_original_json_for_extras_and_extensions)) {
       return false;
     }
   }
@@ -4596,6 +5164,22 @@
   ParseNumberProperty(&light->intensity, err, o, "intensity", false);
   ParseExtensionsProperty(&light->extensions, err, o);
   ParseExtrasProperty(&(light->extras), o);
+
+  if (store_original_json_for_extras_and_extensions) {
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extensions", it)) {
+        light->extensions_json_string = JsonToString(GetValue(it));
+      }
+    }
+    {
+      json_const_iterator it;
+      if (FindMember(o, "extras", it)) {
+        light->extras_json_string = JsonToString(GetValue(it));
+      }
+    }
+  }
+
   return true;
 }
 
@@ -4743,7 +5327,8 @@
     if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
       const json &root = GetValue(it);
 
-      ParseAsset(&model->asset, err, root);
+      ParseAsset(&model->asset, err, root,
+                 store_original_json_for_extras_and_extensions_);
     }
   }
 
@@ -4799,8 +5384,9 @@
         return false;
       }
       Buffer buffer;
-      if (!ParseBuffer(&buffer, err, o, &fs, base_dir, is_binary_, bin_data_,
-                       bin_size_)) {
+      if (!ParseBuffer(&buffer, err, o,
+                       store_original_json_for_extras_and_extensions_, &fs,
+                       base_dir, is_binary_, bin_data_, bin_size_)) {
         return false;
       }
 
@@ -4822,7 +5408,8 @@
         return false;
       }
       BufferView bufferView;
-      if (!ParseBufferView(&bufferView, err, o)) {
+      if (!ParseBufferView(&bufferView, err, o,
+                           store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -4845,7 +5432,8 @@
         return false;
       }
       Accessor accessor;
-      if (!ParseAccessor(&accessor, err, o)) {
+      if (!ParseAccessor(&accessor, err, o,
+                         store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -4868,7 +5456,8 @@
         return false;
       }
       Mesh mesh;
-      if (!ParseMesh(&mesh, model, err, o)) {
+      if (!ParseMesh(&mesh, model, err, o,
+                     store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -4932,7 +5521,8 @@
         return false;
       }
       Node node;
-      if (!ParseNode(&node, err, o)) {
+      if (!ParseNode(&node, err, o,
+                     store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -4965,6 +5555,21 @@
       ParseExtensionsProperty(&scene.extensions, err, o);
       ParseExtrasProperty(&scene.extras, o);
 
+      if (store_original_json_for_extras_and_extensions_) {
+        {
+          json_const_iterator it;
+          if (FindMember(o, "extensions", it)) {
+            model->extensions_json_string = JsonToString(GetValue(it));
+          }
+        }
+        {
+          json_const_iterator it;
+          if (FindMember(o, "extras", it)) {
+            model->extras_json_string = JsonToString(GetValue(it));
+          }
+        }
+      }
+
       model->scenes.emplace_back(std::move(scene));
       return true;
     });
@@ -4995,7 +5600,8 @@
       Material material;
       ParseStringProperty(&material.name, err, o, "name", false);
 
-      if (!ParseMaterial(&material, err, o)) {
+      if (!ParseMaterial(&material, err, o,
+                         store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -5019,8 +5625,9 @@
         return false;
       }
       Image image;
-      if (!ParseImage(&image, idx, err, warn, o, base_dir, &fs,
-                      &this->LoadImageData, load_image_user_data_)) {
+      if (!ParseImage(&image, idx, err, warn, o,
+                      store_original_json_for_extras_and_extensions_, base_dir,
+                      &fs, &this->LoadImageData, load_image_user_data_)) {
         return false;
       }
 
@@ -5084,7 +5691,9 @@
         return false;
       }
       Texture texture;
-      if (!ParseTexture(&texture, err, o, base_dir)) {
+      if (!ParseTexture(&texture, err, o,
+                        store_original_json_for_extras_and_extensions_,
+                        base_dir)) {
         return false;
       }
 
@@ -5107,7 +5716,8 @@
         return false;
       }
       Animation animation;
-      if (!ParseAnimation(&animation, err, o)) {
+      if (!ParseAnimation(&animation, err, o,
+                          store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -5130,7 +5740,8 @@
         return false;
       }
       Skin skin;
-      if (!ParseSkin(&skin, err, o)) {
+      if (!ParseSkin(&skin, err, o,
+                     store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -5153,7 +5764,8 @@
         return false;
       }
       Sampler sampler;
-      if (!ParseSampler(&sampler, err, o)) {
+      if (!ParseSampler(&sampler, err, o,
+                        store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -5176,7 +5788,8 @@
         return false;
       }
       Camera camera;
-      if (!ParseCamera(&camera, err, o)) {
+      if (!ParseCamera(&camera, err, o,
+                       store_original_json_for_extras_and_extensions_)) {
         return false;
       }
 
@@ -5216,7 +5829,8 @@
             auto arrayItEnd(ArrayEnd(lights));
             for (; arrayIt != arrayItEnd; ++arrayIt) {
               Light light;
-              if (!ParseLight(&light, err, *arrayIt)) {
+              if (!ParseLight(&light, err, *arrayIt,
+                              store_original_json_for_extras_and_extensions_)) {
                 return false;
               }
               model->lights.emplace_back(std::move(light));
@@ -5230,6 +5844,11 @@
   // 19. Parse Extras
   ParseExtrasProperty(&model->extras, v);
 
+  if (store_original_json_for_extras_and_extensions_) {
+    model->extras_json_string = JsonToString(v["extras"]);
+    model->extensions_json_string = JsonToString(v["extensions"]);
+  }
+
   return true;
 }
 
@@ -5407,24 +6026,6 @@
 #endif
 }
 
-std::string JsonToString(const json &o, int spacing = -1) {
-#ifdef TINYGLTF_USE_RAPIDJSON
-  using namespace rapidjson;
-  StringBuffer buffer;
-  if (spacing == -1) {
-    Writer<StringBuffer> writer(buffer);
-    o.Accept(writer);
-  } else {
-    PrettyWriter<StringBuffer> writer(buffer);
-    writer.SetIndent(' ', spacing);
-    o.Accept(writer);
-  }
-  return buffer.GetString();
-#else
-  return o.dump(spacing);
-#endif
-}
-
 void JsonAssign(json &dest, const json &src) {
 #ifdef TINYGLTF_USE_RAPIDJSON
   dest.CopyFrom(src, GetAllocator());
@@ -6130,7 +6731,7 @@
   SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
   SerializeExtensionMap(spot.extensions, o);
   if (spot.extras.Type() != NULL_TYPE) {
-	  SerializeValue("extras", spot.extras, o);
+    SerializeValue("extras", spot.extras, o);
   }
 }
 
@@ -6147,7 +6748,7 @@
   }
   SerializeExtensionMap(light.extensions, o);
   if (light.extras.Type() != NULL_TYPE) {
-	  SerializeValue("extras", light.extras, o);
+    SerializeValue("extras", light.extras, o);
   }
 }