# HG changeset patch # User Sebastien Jodogne # Date 1549464316 -3600 # Node ID 810772486249b593b6aec93bff77117a0855fbab # Parent ef4d86d0550346a5f3891c6d32b08a1b53cd73db URI "/instances/.../file" can return DICOMweb JSON or XML, depending on Accept header diff -r ef4d86d05503 -r 810772486249 Core/DicomParsing/DicomWebJsonVisitor.cpp --- a/Core/DicomParsing/DicomWebJsonVisitor.cpp Wed Feb 06 15:21:32 2019 +0100 +++ b/Core/DicomParsing/DicomWebJsonVisitor.cpp Wed Feb 06 15:45:16 2019 +0100 @@ -306,9 +306,11 @@ #if ORTHANC_ENABLE_PUGIXML == 1 - void DicomWebJsonVisitor::FormatXml(pugi::xml_document& target) const + void DicomWebJsonVisitor::FormatXml(std::string& target) const { - DicomWebJsonToXml(target, result_); + pugi::xml_document doc; + DicomWebJsonToXml(doc, result_); + Toolbox::XmlToString(target, doc); } #endif diff -r ef4d86d05503 -r 810772486249 Core/DicomParsing/DicomWebJsonVisitor.h --- a/Core/DicomParsing/DicomWebJsonVisitor.h Wed Feb 06 15:21:32 2019 +0100 +++ b/Core/DicomParsing/DicomWebJsonVisitor.h Wed Feb 06 15:45:16 2019 +0100 @@ -37,10 +37,6 @@ # error Macro ORTHANC_ENABLE_PUGIXML must be defined to use this file #endif -#if ORTHANC_ENABLE_PUGIXML == 1 -# include -#endif - #include "ITagVisitor.h" #include @@ -109,7 +105,7 @@ } #if ORTHANC_ENABLE_PUGIXML == 1 - void FormatXml(pugi::xml_document& target) const; + void FormatXml(std::string& target) const; #endif virtual void VisitNotSupported(const std::vector& parentTags, diff -r ef4d86d05503 -r 810772486249 Core/Enumerations.cpp --- a/Core/Enumerations.cpp Wed Feb 06 15:21:32 2019 +0100 +++ b/Core/Enumerations.cpp Wed Feb 06 15:45:16 2019 +0100 @@ -59,6 +59,8 @@ static const char* const MIME_WOFF = "application/x-font-woff"; static const char* const MIME_XML_2 = "text/xml"; static const char* const MIME_ZIP = "application/zip"; + static const char* const MIME_DICOM_WEB_JSON = "application/dicom+json"; + static const char* const MIME_DICOM_WEB_XML = "application/dicom+xml"; // This function is autogenerated by the script // "Resources/GenerateErrorCodes.py" @@ -1107,6 +1109,12 @@ case MimeType_PrometheusText: // https://prometheus.io/docs/instrumenting/exposition_formats/#text-based-format return "text/plain; version=0.0.4"; + + case MimeType_DicomWebJson: + return MIME_DICOM_WEB_JSON; + + case MimeType_DicomWebXml: + return MIME_DICOM_WEB_XML; default: throw OrthancException(ErrorCode_ParameterOutOfRange); @@ -1716,6 +1724,14 @@ { return MimeType_Woff; } + else if (mime == MIME_DICOM_WEB_JSON) + { + return MimeType_DicomWebJson; + } + else if (mime == MIME_DICOM_WEB_XML) + { + return MimeType_DicomWebXml; + } else { throw OrthancException(ErrorCode_ParameterOutOfRange); diff -r ef4d86d05503 -r 810772486249 Core/Enumerations.h --- a/Core/Enumerations.h Wed Feb 06 15:21:32 2019 +0100 +++ b/Core/Enumerations.h Wed Feb 06 15:45:16 2019 +0100 @@ -104,9 +104,11 @@ MimeType_Svg, MimeType_WebAssembly, MimeType_Xml, - MimeType_Woff, // Web Open Font Format + MimeType_Woff, // Web Open Font Format MimeType_Zip, - MimeType_PrometheusText // Prometheus text-based exposition format (for metrics) + MimeType_PrometheusText, // Prometheus text-based exposition format (for metrics) + MimeType_DicomWebJson, + MimeType_DicomWebXml }; diff -r ef4d86d05503 -r 810772486249 Core/Toolbox.cpp --- a/Core/Toolbox.cpp Wed Feb 06 15:21:32 2019 +0100 +++ b/Core/Toolbox.cpp Wed Feb 06 15:45:16 2019 +0100 @@ -99,7 +99,6 @@ #if ORTHANC_ENABLE_PUGIXML == 1 # include "ChunkedBuffer.h" -# include #endif @@ -1023,11 +1022,16 @@ decl.append_attribute("version").set_value("1.0"); decl.append_attribute("encoding").set_value("utf-8"); + XmlToString(target, doc); + } + + void Toolbox::XmlToString(std::string& target, + const pugi::xml_document& source) + { ChunkedBufferWriter writer; - doc.save(writer, " ", pugi::format_default, pugi::encoding_utf8); + source.save(writer, " ", pugi::format_default, pugi::encoding_utf8); writer.Flatten(target); } - #endif diff -r ef4d86d05503 -r 810772486249 Core/Toolbox.h --- a/Core/Toolbox.h Wed Feb 06 15:21:32 2019 +0100 +++ b/Core/Toolbox.h Wed Feb 06 15:45:16 2019 +0100 @@ -68,6 +68,10 @@ **/ +#if ORTHANC_ENABLE_PUGIXML == 1 +# include +#endif + namespace Orthanc { @@ -192,6 +196,11 @@ const std::string& arrayElement = "item"); #endif +#if ORTHANC_ENABLE_PUGIXML == 1 + void XmlToString(std::string& target, + const pugi::xml_document& source); +#endif + bool IsInteger(const std::string& str); void CopyJsonWithoutComments(Json::Value& target, diff -r ef4d86d05503 -r 810772486249 NEWS --- a/NEWS Wed Feb 06 15:21:32 2019 +0100 +++ b/NEWS Wed Feb 06 15:45:16 2019 +0100 @@ -13,6 +13,8 @@ -------- * API version has been upgraded to 1.4 +* URI "/instances/.../file" can return DICOMweb JSON or XML, depending + on the content of the "Accept" HTTP header * New URI "/tools/metrics" to dynamically enable/disable the collection of metrics * New URI "/tools/metrics-prometheus" to retrieve metrics using Prometheus text format * URI "/peers?expand" provides more information about the peers diff -r ef4d86d05503 -r 810772486249 OrthancServer/OrthancRestApi/OrthancRestResources.cpp --- a/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Feb 06 15:21:32 2019 +0100 +++ b/OrthancServer/OrthancRestApi/OrthancRestResources.cpp Wed Feb 06 15:45:16 2019 +0100 @@ -35,6 +35,7 @@ #include "OrthancRestApi.h" #include "../../Core/Compression/GzipCompressor.h" +#include "../../Core/DicomParsing/DicomWebJsonVisitor.h" #include "../../Core/DicomParsing/FromDcmtkBridge.h" #include "../../Core/DicomParsing/Internals/DicomImageDecoder.h" #include "../../Core/HttpServer/HttpContentNegociation.h" @@ -244,6 +245,45 @@ ServerContext& context = OrthancRestApi::GetContext(call); std::string publicId = call.GetUriComponent("id", ""); + + IHttpHandler::Arguments::const_iterator accept = call.GetHttpHeaders().find("accept"); + if (accept != call.GetHttpHeaders().end()) + { + // New in Orthanc 1.5.4 + try + { + MimeType mime = StringToMimeType(accept->second.c_str()); + + if (mime == MimeType_DicomWebJson || + mime == MimeType_DicomWebXml) + { + DicomWebJsonVisitor visitor; + + { + ServerContext::DicomCacheLocker locker(OrthancRestApi::GetContext(call), publicId); + locker.GetDicom().Apply(visitor); + } + + if (mime == MimeType_DicomWebJson) + { + std::string s = visitor.GetResult().toStyledString(); + call.GetOutput().AnswerBuffer(s, MimeType_DicomWebJson); + } + else + { + std::string xml; + visitor.FormatXml(xml); + call.GetOutput().AnswerBuffer(xml, MimeType_DicomWebXml); + } + + return; + } + } + catch (OrthancException&) + { + } + } + context.AnswerAttachment(call.GetOutput(), publicId, FileContentType_Dicom); } diff -r ef4d86d05503 -r 810772486249 UnitTestsSources/DicomMapTests.cpp --- a/UnitTestsSources/DicomMapTests.cpp Wed Feb 06 15:21:32 2019 +0100 +++ b/UnitTestsSources/DicomMapTests.cpp Wed Feb 06 15:45:16 2019 +0100 @@ -586,7 +586,7 @@ ASSERT_EQ(1u, tag.getMemberNames().size()); } - pugi::xml_document xml; + std::string xml; visitor.FormatXml(xml); } @@ -616,7 +616,7 @@ ASSERT_FLOAT_EQ(2.5f, value[3].asFloat()); } - pugi::xml_document xml; + std::string xml; visitor.FormatXml(xml); } @@ -758,7 +758,7 @@ ASSERT_EQ("UT", visitor.GetResult() ["00400031"]["Value"][0].asString()); - pugi::xml_document xml; + std::string xml; visitor.FormatXml(xml); } @@ -802,6 +802,6 @@ ASSERT_TRUE(items.find("item1") != items.end()); ASSERT_TRUE(items.find("item2") != items.end()); - pugi::xml_document xml; + std::string xml; visitor.FormatXml(xml); } diff -r ef4d86d05503 -r 810772486249 UnitTestsSources/UnitTestsMain.cpp --- a/UnitTestsSources/UnitTestsMain.cpp Wed Feb 06 15:21:32 2019 +0100 +++ b/UnitTestsSources/UnitTestsMain.cpp Wed Feb 06 15:45:16 2019 +0100 @@ -756,6 +756,8 @@ ASSERT_EQ(MimeType_Xml, StringToMimeType("application/xml")); ASSERT_EQ(MimeType_Xml, StringToMimeType("text/xml")); ASSERT_EQ(MimeType_Xml, StringToMimeType(EnumerationToString(MimeType_Xml))); + ASSERT_EQ(MimeType_DicomWebJson, StringToMimeType(EnumerationToString(MimeType_DicomWebJson))); + ASSERT_EQ(MimeType_DicomWebXml, StringToMimeType(EnumerationToString(MimeType_DicomWebXml))); ASSERT_THROW(StringToMimeType("nope"), OrthancException); ASSERT_TRUE(IsResourceLevelAboveOrEqual(ResourceType_Patient, ResourceType_Patient));