changeset 1820:2d8191b13567

integration worklists->mainline
author Sebastien Jodogne <s.jodogne@gmail.com>
date Tue, 24 Nov 2015 17:24:37 +0100
parents 87c069c94ac9 (current diff) 84f0a118a72c (diff)
children 580951a33583
files
diffstat 12 files changed, 298 insertions(+), 91 deletions(-) [+]
line wrap: on
line diff
--- a/Core/DicomFormat/DicomTag.h	Tue Nov 24 13:36:08 2015 +0100
+++ b/Core/DicomFormat/DicomTag.h	Tue Nov 24 17:24:37 2015 +0100
@@ -66,6 +66,11 @@
       return element_;
     }
 
+    bool IsPrivate() const
+    {
+      return group_ % 2 == 1;
+    }
+
     const char* GetMainTagsName() const;
 
     bool operator< (const DicomTag& other) const;
--- a/Core/Toolbox.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/Core/Toolbox.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -554,6 +554,14 @@
   }
 #  endif
 
+
+  void Toolbox::EncodeDataUriScheme(std::string& result,
+                                    const std::string& mime,
+                                    const std::string& content)
+  {
+    result = "data:" + mime + ";base64," + base64_encode(content);
+  }
+
 #endif
 
 
--- a/Core/Toolbox.h	Tue Nov 24 13:36:08 2015 +0100
+++ b/Core/Toolbox.h	Tue Nov 24 17:24:37 2015 +0100
@@ -123,6 +123,10 @@
                              std::string& content,
                              const std::string& source);
 #  endif
+
+    void EncodeDataUriScheme(std::string& result,
+                             const std::string& mime,
+                             const std::string& content);
 #endif
 
     std::string GetPathToExecutable();
--- a/OrthancServer/DicomModification.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/DicomModification.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -161,7 +161,7 @@
     removals_.erase(tag);
     RemoveInternal(tag);
 
-    if (FromDcmtkBridge::IsPrivateTag(tag))
+    if (tag.IsPrivate())
     {
       privateTagsToKeep_.insert(tag);
     }
--- a/OrthancServer/FromDcmtkBridge.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/FromDcmtkBridge.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -354,19 +354,6 @@
   }
 
 
-  bool FromDcmtkBridge::IsPrivateTag(const DicomTag& tag)
-  {
-#if 1
-    DcmTagKey tmp(tag.GetGroup(), tag.GetElement());
-    return tmp.isPrivate();
-#else
-    // Implementation for Orthanc versions <= 0.8.5
-    DcmTag tmp(tag.GetGroup(), tag.GetElement());
-    return IsPrivateTag(tmp);
-#endif
-  }
-
-
   DicomValue* FromDcmtkBridge::ConvertLeafElement(DcmElement& element,
                                                   DicomToJsonFlags flags,
                                                   Encoding encoding)
@@ -377,25 +364,19 @@
       throw OrthancException(ErrorCode_BadParameterType);
     }
 
-    if (element.isaString())
+    char *c = NULL;
+    if (element.isaString() &&
+        element.getString(c).good())
     {
-      char *c;
-      if (element.getString(c).good())
+      if (c == NULL)  // This case corresponds to the empty string
       {
-        if (c == NULL)  // This case corresponds to the empty string
-        {
-          return new DicomValue("", false);
-        }
-        else
-        {
-          std::string s(c);
-          std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
-          return new DicomValue(utf8, false);
-        }
+        return new DicomValue("", false);
       }
       else
       {
-        return new DicomValue;
+        std::string s(c);
+        std::string utf8 = Toolbox::ConvertToUtf8(s, encoding);
+        return new DicomValue(utf8, false);
       }
     }
 
@@ -414,24 +395,6 @@
         case EVR_OW:  // other word
         case EVR_UN:  // unknown value representation
         case EVR_ox:  // OB or OW depending on context
-        {
-          if (!(flags & DicomToJsonFlags_ConvertBinaryToNull))
-          {
-            Uint8* data = NULL;
-            if (element.getUint8Array(data) == EC_Normal)
-            {
-              return new DicomValue(reinterpret_cast<const char*>(data), element.getLength(), true);
-            }
-          }
-
-          return new DicomValue;
-        }
-    
-          /**
-           * String types, should never happen at this point because of
-           * "element.isaString()".
-           **/
-      
         case EVR_DS:  // decimal string
         case EVR_IS:  // integer string
         case EVR_AS:  // age string
@@ -447,12 +410,24 @@
         case EVR_UT:  // unlimited text
         case EVR_PN:  // person name
         case EVR_UI:  // unique identifier
-          return new DicomValue;
-
+        case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
+        case EVR_UNKNOWN2B:  // used internally for elements with unknown VR with 2-byte length field in explicit VR
+        {
+          if (!(flags & DicomToJsonFlags_ConvertBinaryToNull))
+          {
+            Uint8* data = NULL;
+            if (element.getUint8Array(data) == EC_Normal)
+            {
+              return new DicomValue(reinterpret_cast<const char*>(data), element.getLength(), true);
+            }
+          }
 
-          /**
-           * Numberic types
-           **/ 
+          return new DicomValue;
+        }
+    
+        /**
+         * Numberic types
+         **/ 
       
         case EVR_SL:  // signed long
         {
@@ -553,10 +528,8 @@
         case EVR_dirRecord:  // used internally for DICOMDIR records
         case EVR_pixelSQ:  // used internally for pixel sequences in a compressed image
         case EVR_pixelItem:  // used internally for pixel items in a compressed image
-        case EVR_UNKNOWN: // used internally for elements with unknown VR (encoded with 4-byte length field in explicit VR)
         case EVR_PixelData:  // used internally for uncompressed pixeld data
         case EVR_OverlayData:  // used internally for overlay data
-        case EVR_UNKNOWN2B:  // used internally for elements with unknown VR with 2-byte length field in explicit VR
           return new DicomValue;
 
 
@@ -782,8 +755,11 @@
         throw OrthancException(ErrorCode_InternalError);
       }
 
-      if (!(flags & DicomToJsonFlags_IncludePrivateTags) &&
-          element->getTag().isPrivate())
+      DicomTag tag(FromDcmtkBridge::Convert(element->getTag()));
+
+      /*element->getTag().isPrivate()*/
+      if (tag.IsPrivate() &&
+          !(flags & DicomToJsonFlags_IncludePrivateTags))    
       {
         continue;
       }
@@ -805,8 +781,6 @@
           evr == EVR_ox)
       {
         // This is a binary tag
-        DicomTag tag(FromDcmtkBridge::Convert(element->getTag()));
-
         if ((tag == DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludePixelData)) ||
             (tag != DICOM_TAG_PIXEL_DATA && !(flags & DicomToJsonFlags_IncludeBinary)))
         {
@@ -1084,8 +1058,7 @@
 
   static bool IsBinaryTag(const DcmTag& key)
   {
-    return (key.isPrivate() || 
-            key.isUnknownVR() || 
+    return (key.isUnknownVR() || 
             key.getEVR() == EVR_OB ||
             key.getEVR() == EVR_OF ||
             key.getEVR() == EVR_OW ||
@@ -1098,7 +1071,8 @@
   {
     DcmTag key(tag.GetGroup(), tag.GetElement());
 
-    if (IsBinaryTag(key))
+    if (tag.IsPrivate() ||
+        IsBinaryTag(key))
     {
       return new DcmOtherByteOtherWord(key);
     }
@@ -1241,13 +1215,13 @@
   void FromDcmtkBridge::FillElementWithString(DcmElement& element,
                                               const DicomTag& tag,
                                               const std::string& utf8Value,
-                                              bool decodeBinaryTags,
+                                              bool decodeDataUriScheme,
                                               Encoding dicomEncoding)
   {
     std::string binary;
     const std::string* decoded = &utf8Value;
 
-    if (decodeBinaryTags &&
+    if (decodeDataUriScheme &&
         boost::starts_with(utf8Value, "data:application/octet-stream;base64,"))
     {
       std::string mime;
@@ -1262,7 +1236,8 @@
 
     DcmTag key(tag.GetGroup(), tag.GetElement());
 
-    if (IsBinaryTag(key))
+    if (tag.IsPrivate() ||
+        IsBinaryTag(key))
     {
       if (element.putUint8Array((const Uint8*) decoded->c_str(), decoded->size()).good())
       {
@@ -1412,7 +1387,7 @@
 
   DcmElement* FromDcmtkBridge::FromJson(const DicomTag& tag,
                                         const Json::Value& value,
-                                        bool decodeBinaryTags,
+                                        bool decodeDataUriScheme,
                                         Encoding dicomEncoding)
   {
     std::auto_ptr<DcmElement> element;
@@ -1421,7 +1396,7 @@
     {
       case Json::stringValue:
         element.reset(CreateElementForTag(tag));
-        FillElementWithString(*element, tag, value.asString(), decodeBinaryTags, dicomEncoding);
+        FillElementWithString(*element, tag, value.asString(), decodeDataUriScheme, dicomEncoding);
         break;
 
       case Json::arrayValue:
@@ -1442,7 +1417,7 @@
           Json::Value::Members members = value[i].getMemberNames();
           for (Json::Value::ArrayIndex j = 0; j < members.size(); j++)
           {
-            item->insert(FromJson(ParseTag(members[j]), value[i][members[j]], decodeBinaryTags, dicomEncoding));
+            item->insert(FromJson(ParseTag(members[j]), value[i][members[j]], decodeDataUriScheme, dicomEncoding));
           }
 
           sequence->append(item.release());
--- a/OrthancServer/FromDcmtkBridge.h	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/FromDcmtkBridge.h	Tue Nov 24 17:24:37 2015 +0100
@@ -60,8 +60,6 @@
 
     static DicomTag GetTag(const DcmElement& element);
 
-    static bool IsPrivateTag(const DicomTag& tag);
-
     static bool IsUnknownTag(const DicomTag& tag);
 
     static DicomValue* ConvertLeafElement(DcmElement& element,
@@ -125,12 +123,12 @@
     static void FillElementWithString(DcmElement& element,
                                       const DicomTag& tag,
                                       const std::string& utf8alue,  // Encoded using UTF-8
-                                      bool interpretBinaryTags,
+                                      bool decodeDataUriScheme,
                                       Encoding dicomEncoding);
 
     static DcmElement* FromJson(const DicomTag& tag,
                                 const Json::Value& element,  // Encoding using UTF-8
-                                bool interpretBinaryTags,
+                                bool decodeDataUriScheme,
                                 Encoding dicomEncoding);
 
     static DcmEVR ParseValueRepresentation(const std::string& s);
--- a/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/OrthancRestApi/OrthancRestModalities.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -865,6 +865,32 @@
   }
 
 
+  static void DicomFindWorklist(RestApiPostCall& call)
+  {
+    ServerContext& context = OrthancRestApi::GetContext(call);
+
+    Json::Value json;
+    if (call.ParseJsonRequest(json))
+    {
+      const std::string& localAet = context.GetDefaultLocalApplicationEntityTitle();
+      RemoteModalityParameters remote = Configuration::GetModalityUsingSymbolicName(call.GetUriComponent("id", ""));
+
+      std::auto_ptr<ParsedDicomFile> query(ParsedDicomFile::CreateFromJson(json, static_cast<DicomFromJsonFlags>(0)));
+
+      DicomFindAnswers answers;
+
+      {
+        ReusableDicomUserConnection::Locker locker(context.GetReusableDicomUserConnection(), localAet, remote);
+        locker.GetConnection().FindWorklist(answers, *query);
+      }
+
+      Json::Value result;
+      answers.ToJson(result, true);
+      call.GetOutput().AnswerJson(result);
+    }
+  }
+
+
   void OrthancRestApi::RegisterModalities()
   {
     Register("/modalities", ListModalities);
@@ -898,5 +924,7 @@
     Register("/peers/{id}", UpdatePeer);
     Register("/peers/{id}", DeletePeer);
     Register("/peers/{id}/store", PeerStore);
+
+    Register("/modalities/{id}/find-worklist", DicomFindWorklist);
   }
 }
--- a/OrthancServer/ParsedDicomFile.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -80,6 +80,7 @@
 
 #include "ParsedDicomFile.h"
 
+#include "OrthancInitialization.h"
 #include "ServerToolbox.h"
 #include "FromDcmtkBridge.h"
 #include "ToDcmtkBridge.h"
@@ -593,9 +594,9 @@
 
   void ParsedDicomFile::Insert(const DicomTag& tag,
                                const Json::Value& value,
-                               bool decodeBinaryTags)
+                               bool decodeDataUriScheme)
   {
-    std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeBinaryTags, GetEncoding()));
+    std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding()));
     InsertInternal(*pimpl_->file_->getDataset(), element.release());
   }
 
@@ -630,7 +631,7 @@
 
   void ParsedDicomFile::UpdateStorageUid(const DicomTag& tag,
                                          const std::string& utf8Value,
-                                         bool decodeBinaryTags)
+                                         bool decodeDataUriScheme)
   {
     if (tag != DICOM_TAG_SOP_CLASS_UID &&
         tag != DICOM_TAG_SOP_INSTANCE_UID)
@@ -641,7 +642,7 @@
     std::string binary;
     const std::string* decoded = &utf8Value;
 
-    if (decodeBinaryTags &&
+    if (decodeDataUriScheme &&
         boost::starts_with(utf8Value, "data:application/octet-stream;base64,"))
     {
       std::string mime;
@@ -692,10 +693,10 @@
     
   void ParsedDicomFile::Replace(const DicomTag& tag,
                                 const Json::Value& value,
-                                bool decodeBinaryTags,
+                                bool decodeDataUriScheme,
                                 DicomReplaceMode mode)
   {
-    std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeBinaryTags, GetEncoding()));
+    std::auto_ptr<DcmElement> element(FromDcmtkBridge::FromJson(tag, value, decodeDataUriScheme, GetEncoding()));
     ReplaceInternal(*pimpl_->file_->getDataset(), element, mode);
 
     if (tag == DICOM_TAG_SOP_CLASS_UID ||
@@ -706,7 +707,7 @@
         throw OrthancException(ErrorCode_BadParameterType);
       }
 
-      UpdateStorageUid(tag, value.asString(), decodeBinaryTags);
+      UpdateStorageUid(tag, value.asString(), decodeDataUriScheme);
     }
   }
 
@@ -728,7 +729,7 @@
     DcmTagKey k(tag.GetGroup(), tag.GetElement());
     DcmDataset& dataset = *pimpl_->file_->getDataset();
 
-    if (FromDcmtkBridge::IsPrivateTag(tag) ||
+    if (tag.IsPrivate() ||
         FromDcmtkBridge::IsUnknownTag(tag) ||
         tag == DICOM_TAG_PIXEL_DATA ||
         tag == DICOM_TAG_ENCAPSULATED_DOCUMENT)
@@ -1252,4 +1253,60 @@
   {
     FromDcmtkBridge::Convert(tags, *pimpl_->file_->getDataset());
   }
+
+
+  ParsedDicomFile* ParsedDicomFile::CreateFromJson(const Json::Value& json,
+                                                   DicomFromJsonFlags flags)
+  {
+    std::string tmp = Configuration::GetGlobalStringParameter("DefaultEncoding", "Latin1");
+    Encoding encoding = StringToEncoding(tmp.c_str());
+
+    Json::Value::Members tags = json.getMemberNames();
+    
+    for (size_t i = 0; i < tags.size(); i++)
+    {
+      DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
+      if (tag == DICOM_TAG_SPECIFIC_CHARACTER_SET)
+      {
+        const Json::Value& value = json[tags[i]];
+        if (value.type() != Json::stringValue ||
+            !GetDicomEncoding(encoding, value.asCString()))
+        {
+          LOG(ERROR) << "Unknown encoding while creating DICOM from JSON: " << value;
+          throw OrthancException(ErrorCode_BadRequest);
+        }
+      }
+    }
+
+    const bool generateIdentifiers = (flags & DicomFromJsonFlags_GenerateIdentifiers);
+    const bool decodeDataUriScheme = (flags & DicomFromJsonFlags_DecodeDataUriScheme);
+
+    std::auto_ptr<ParsedDicomFile> result(new ParsedDicomFile(generateIdentifiers));
+    result->SetEncoding(encoding);
+
+    for (size_t i = 0; i < tags.size(); i++)
+    {
+      DicomTag tag = FromDcmtkBridge::ParseTag(tags[i]);
+      const Json::Value& value = json[tags[i]];
+
+      if (tag == DICOM_TAG_PIXEL_DATA ||
+          tag == DICOM_TAG_ENCAPSULATED_DOCUMENT)
+      {
+        if (value.type() != Json::stringValue)
+        {
+          throw OrthancException(ErrorCode_BadRequest);
+        }
+        else
+        {
+          result->EmbedContent(value.asString());
+        }
+      }
+      else if (tag != DICOM_TAG_SPECIFIC_CHARACTER_SET)
+      {
+        result->Replace(tag, value, decodeDataUriScheme);
+      }
+    }
+
+    return result.release();
+  }
 }
--- a/OrthancServer/ParsedDicomFile.h	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/ParsedDicomFile.h	Tue Nov 24 17:24:37 2015 +0100
@@ -59,7 +59,7 @@
 
     void UpdateStorageUid(const DicomTag& tag,
                           const std::string& value,
-                          bool decodeBinaryTags);
+                          bool decodeDataUriScheme);
 
   public:
     ParsedDicomFile(bool createIdentifiers);  // Create a minimal DICOM instance
@@ -94,12 +94,12 @@
 
     void Replace(const DicomTag& tag,
                  const Json::Value& value,  // Assumed to be encoded with UTF-8
-                 bool decodeBinaryTags,
+                 bool decodeDataUriScheme,
                  DicomReplaceMode mode = DicomReplaceMode_InsertIfAbsent);
 
     void Insert(const DicomTag& tag,
                 const Json::Value& value,   // Assumed to be encoded with UTF-8
-                bool decodeBinaryTags);
+                bool decodeDataUriScheme);
 
     void RemovePrivateTags()
     {
@@ -159,6 +159,9 @@
     bool ExtractPdf(std::string& pdf);
 
     void Convert(DicomMap& tags);
+
+    static ParsedDicomFile* CreateFromJson(const Json::Value& value,
+                                           DicomFromJsonFlags flags);
   };
 
 }
--- a/OrthancServer/ServerEnumerations.h	Tue Nov 24 13:36:08 2015 +0100
+++ b/OrthancServer/ServerEnumerations.h	Tue Nov 24 17:24:37 2015 +0100
@@ -127,6 +127,12 @@
                                  DicomToJsonFlags_ConvertBinaryToNull)
   };
 
+  enum DicomFromJsonFlags
+  {
+    DicomFromJsonFlags_DecodeDataUriScheme = (1 << 0),
+    DicomFromJsonFlags_GenerateIdentifiers = (1 << 1)
+  };
+
   enum IdentifierConstraintType
   {
     IdentifierConstraintType_Equal,
--- a/Plugins/Engine/OrthancPlugins.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/Plugins/Engine/OrthancPlugins.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -363,7 +363,7 @@
       Reset();
     }
 
-    void GetQueryDicom(OrthancPluginMemoryBuffer& target) const
+    void GetDicomQuery(OrthancPluginMemoryBuffer& target) const
     {
       assert(currentQuery_ != NULL);
       std::string dicom;
@@ -1966,7 +1966,7 @@
       {
         const _OrthancPluginWorklistQueryOperation& p =
           *reinterpret_cast<const _OrthancPluginWorklistQueryOperation*>(parameters);
-        reinterpret_cast<const WorklistHandler*>(p.query)->GetQueryDicom(*p.target);
+        reinterpret_cast<const WorklistHandler*>(p.query)->GetDicomQuery(*p.target);
         return true;
       }
 
--- a/UnitTestsSources/FromDcmtkTests.cpp	Tue Nov 24 13:36:08 2015 +0100
+++ b/UnitTestsSources/FromDcmtkTests.cpp	Tue Nov 24 17:24:37 2015 +0100
@@ -97,8 +97,8 @@
 
   const DicomTag privateTag(0x0045, 0x0010);
   const DicomTag privateTag2(FromDcmtkBridge::ParseTag("0031-1020"));
-  ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag));
-  ASSERT_TRUE(FromDcmtkBridge::IsPrivateTag(privateTag2));
+  ASSERT_TRUE(privateTag.IsPrivate());
+  ASSERT_TRUE(privateTag2.IsPrivate());
   ASSERT_EQ(0x0031, privateTag2.GetGroup());
   ASSERT_EQ(0x1020, privateTag2.GetElement());
 
@@ -543,6 +543,15 @@
   ASSERT_EQ(Json::stringValue, v["7050,1000"].type());
   ASSERT_EQ("Some public tag", v["7050,1000"].asString());
 
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(7, v.getMemberNames().size());
+  ASSERT_FALSE(v.isMember("7052,1000"));
+  ASSERT_TRUE(v.isMember("7050,1000"));
+  ASSERT_TRUE(v.isMember("7053,1000"));
+  ASSERT_EQ("Some public tag", v["7050,1000"].asString());
+  ASSERT_EQ(Json::nullValue, v["7053,1000"].type());
+
   f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludePrivateTags, 0);
   ASSERT_EQ(Json::objectValue, v.type());
   ASSERT_EQ(7, v.getMemberNames().size());
@@ -550,26 +559,42 @@
   ASSERT_TRUE(v.isMember("7050,1000"));
   ASSERT_TRUE(v.isMember("7053,1000"));
   ASSERT_EQ("Some public tag", v["7050,1000"].asString());
-  ASSERT_EQ(Json::nullValue, v["7053,1000"].type());  // TODO SHOULD BE STRING
+  std::string mime, content;
+  ASSERT_EQ(Json::stringValue, v["7053,1000"].type());
+  Toolbox::DecodeDataUriScheme(mime, content, v["7053,1000"].asString());
+  ASSERT_EQ("application/octet-stream", mime);
+  ASSERT_EQ("Some private tag", content);
 
-  f.ToJson(v, DicomToJsonFormat_Short, DicomToJsonFlags_IncludeUnknownTags, 0);
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
   ASSERT_EQ(Json::objectValue, v.type());
   ASSERT_EQ(7, v.getMemberNames().size());
   ASSERT_TRUE(v.isMember("7050,1000"));
   ASSERT_TRUE(v.isMember("7052,1000"));
   ASSERT_FALSE(v.isMember("7053,1000"));
   ASSERT_EQ("Some public tag", v["7050,1000"].asString());
-  ASSERT_EQ(Json::nullValue, v["7052,1000"].type());  // TODO SHOULD BE STRING
+  ASSERT_EQ(Json::nullValue, v["7052,1000"].type());
 
-  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags), 0);
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags), 0);
+  ASSERT_EQ(Json::objectValue, v.type());
+  ASSERT_EQ(7, v.getMemberNames().size());
+  ASSERT_TRUE(v.isMember("7050,1000"));
+  ASSERT_TRUE(v.isMember("7052,1000"));
+  ASSERT_FALSE(v.isMember("7053,1000"));
+  ASSERT_EQ("Some public tag", v["7050,1000"].asString());
+  ASSERT_EQ(Json::stringValue, v["7052,1000"].type());
+  Toolbox::DecodeDataUriScheme(mime, content, v["7052,1000"].asString());
+  ASSERT_EQ("application/octet-stream", mime);
+  ASSERT_EQ("Some unknown tag", content);
+
+  f.ToJson(v, DicomToJsonFormat_Short, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeUnknownTags | DicomToJsonFlags_IncludePrivateTags | DicomToJsonFlags_ConvertBinaryToNull), 0);
   ASSERT_EQ(Json::objectValue, v.type());
   ASSERT_EQ(8, v.getMemberNames().size());
   ASSERT_TRUE(v.isMember("7050,1000"));
   ASSERT_TRUE(v.isMember("7052,1000"));
   ASSERT_TRUE(v.isMember("7053,1000"));
   ASSERT_EQ("Some public tag", v["7050,1000"].asString());
-  ASSERT_EQ(Json::nullValue, v["7052,1000"].type());  // TODO SHOULD BE STRING
-  ASSERT_EQ(Json::nullValue, v["7053,1000"].type());  // TODO SHOULD BE STRING
+  ASSERT_EQ(Json::nullValue, v["7052,1000"].type());
+  ASSERT_EQ(Json::nullValue, v["7053,1000"].type());
 }
 
 
@@ -637,3 +662,101 @@
 
   //std::cout << j;
 }
+
+
+TEST(ParsedDicomFile, FromJson)
+{
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7057, 0x1000), EVR_OB, "MyPrivateTag", 1, 1);
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7059, 0x1000), EVR_OB, "MyPrivateTag", 1, 1);
+  FromDcmtkBridge::RegisterDictionaryTag(DicomTag(0x7050, 0x1000), EVR_PN, "Declared public tag", 1, 1);
+
+  Json::Value v;
+  const std::string sopClassUid = "1.2.840.10008.5.1.4.1.1.1";  // CR Image Storage:
+
+  {
+    v["SOPClassUID"] = sopClassUid;
+    v["SpecificCharacterSet"] = "ISO_IR 148";    // This is latin-5
+    v["PatientName"] = "Sébastien";
+    v["7050-1000"] = "Some public tag";  // Even group => public tag
+    v["7052-1000"] = "Some unknown tag";  // Even group => public, unknown tag
+    v["7057-1000"] = "Some private tag";  // Odd group => private tag
+    v["7059-1000"] = "Some private tag2";  // Odd group => private tag, with an odd length to test padding
+  
+    std::string s;
+    Toolbox::EncodeDataUriScheme(s, "application/octet-stream", "Sebastien");
+    v["StudyDescription"] = s;
+
+    v["PixelData"] = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==";  // A red dot of 5x5 pixels
+    v["0040,0100"] = Json::arrayValue;  // ScheduledProcedureStepSequence
+
+    Json::Value vv;
+    vv["Modality"] = "MR";
+    v["0040,0100"].append(vv);
+
+    vv["Modality"] = "CT";
+    v["0040,0100"].append(vv);
+  }
+
+  const DicomToJsonFlags toJsonFlags = static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludeBinary |
+                                                                     DicomToJsonFlags_IncludePixelData | 
+                                                                     DicomToJsonFlags_IncludePrivateTags | 
+                                                                     DicomToJsonFlags_IncludeUnknownTags | 
+                                                                     DicomToJsonFlags_ConvertBinaryToAscii);
+
+
+  {
+    std::auto_ptr<ParsedDicomFile> dicom
+      (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers)));
+
+    Json::Value vv;
+    dicom->ToJson(vv, DicomToJsonFormat_Simple, toJsonFlags, 0);
+
+    ASSERT_EQ(vv["SOPClassUID"].asString(), sopClassUid);
+    ASSERT_EQ(vv["MediaStorageSOPClassUID"].asString(), sopClassUid);
+    ASSERT_TRUE(vv.isMember("SOPInstanceUID"));
+    ASSERT_TRUE(vv.isMember("SeriesInstanceUID"));
+    ASSERT_TRUE(vv.isMember("StudyInstanceUID"));
+    ASSERT_TRUE(vv.isMember("PatientID"));
+  }
+
+
+  {
+    std::auto_ptr<ParsedDicomFile> dicom
+      (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_GenerateIdentifiers)));
+
+    Json::Value vv;
+    dicom->ToJson(vv, DicomToJsonFormat_Simple, static_cast<DicomToJsonFlags>(DicomToJsonFlags_IncludePixelData), 0);
+
+    std::string mime, content;
+    Toolbox::DecodeDataUriScheme(mime, content, vv["PixelData"].asString());
+    ASSERT_EQ("application/octet-stream", mime);
+    ASSERT_EQ(5u * 5u * 3u /* the red dot is 5x5 pixels in RGB24 */ + 1 /* for padding */, content.size());
+  }
+
+
+  {
+    std::auto_ptr<ParsedDicomFile> dicom
+      (ParsedDicomFile::CreateFromJson(v, static_cast<DicomFromJsonFlags>(DicomFromJsonFlags_DecodeDataUriScheme)));
+
+    Json::Value vv;
+    dicom->ToJson(vv, DicomToJsonFormat_Short, toJsonFlags, 0);
+
+    ASSERT_FALSE(vv.isMember("SOPInstanceUID"));
+    ASSERT_FALSE(vv.isMember("SeriesInstanceUID"));
+    ASSERT_FALSE(vv.isMember("StudyInstanceUID"));
+    ASSERT_FALSE(vv.isMember("PatientID"));
+    ASSERT_EQ(2u, vv["0040,0100"].size());
+    ASSERT_EQ("MR", vv["0040,0100"][0]["0008,0060"].asString());
+    ASSERT_EQ("CT", vv["0040,0100"][1]["0008,0060"].asString());
+    ASSERT_EQ("Some public tag", vv["7050,1000"].asString());
+    ASSERT_EQ("Some unknown tag", vv["7052,1000"].asString());
+    ASSERT_EQ("Some private tag", vv["7057,1000"].asString());
+    ASSERT_EQ("Some private tag2", vv["7059,1000"].asString());
+    ASSERT_EQ("Sébastien", vv["0010,0010"].asString());
+    ASSERT_EQ("Sebastien", vv["0008,1030"].asString());
+    ASSERT_EQ("ISO_IR 148", vv["0008,0005"].asString());
+    ASSERT_EQ("5", vv[DICOM_TAG_ROWS.Format()].asString());
+    ASSERT_EQ("5", vv[DICOM_TAG_COLUMNS.Format()].asString());
+    ASSERT_TRUE(vv[DICOM_TAG_PIXEL_DATA.Format()].asString().empty());
+  }
+}