diff --git a/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt b/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt
index 65a22716b089dcebad8fd64d52a6fe5d83534bc1..c81bd112bc874c9e425ff97cbe6b99927c586bf1 100644
--- a/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt
+++ b/Data/Baseline/OTB/Files/ioTvcoreImageMetadataTest.txt
@@ -56,3 +56,28 @@ LUT1D.ARRAY = 1 2 3"
 "NoData": "-10000",
 },]
 }
+md5: {{"Extra.Comment": "Test Extrakeys",
+"ProductType": "Official",
+"ProjectionEPSG": "4326",
+"ProjectionWKT": "UTM projRef",
+"ProjectionProj": "+proj=longlat +datum=WGS84 +no_defs ",
+"SpectralSensitivity": "LUT1D.DIM0.SIZE = 3
+LUT1D.DIM0.ORIGIN = 0
+LUT1D.DIM0.SPACING = 1
+LUT1D.ARRAY = 1 2 3"
+"RPC": "<RPCParam>",
+"ProductionDate": "2009-08-10T10:30:08.142149Z",
+"GCP": "<GCPParam>",
+"SensorID": "PHR",
+}Bands[{"PhysicalBias": "1",
+"BandName": "B3",
+"PhysicalGain": "2",
+},{"PhysicalBias": "2",
+"BandName": "B2",
+"PhysicalGain": "3",
+},{"PhysicalBias": "3",
+"PhysicalGain": "4",
+"BandName": "B4",
+"NoData": "-10000",
+},]
+}
diff --git a/Modules/Core/Metadata/include/otbImageMetadata.h b/Modules/Core/Metadata/include/otbImageMetadata.h
index 87ba36bc00d264fcb1977c86c5fee4017e07aeab..abd36d59eb523c6cf858bc7bf68160de506bb12c 100644
--- a/Modules/Core/Metadata/include/otbImageMetadata.h
+++ b/Modules/Core/Metadata/include/otbImageMetadata.h
@@ -24,6 +24,7 @@
 #include "otbGeometryMetadata.h"
 #include "otbMetaDataKey.h"
 #include "OTBMetadataExport.h"
+#include "otbMacro.h"
 
 #include <boost/any.hpp>
 #include <vector>
@@ -237,6 +238,9 @@ public:
 class OTBMetadata_EXPORT ImageMetadata: public ImageMetadataBase
 {
 public:
+  /** Metadata object as a vector of Keywordlist */
+  using KeywordlistVector = std::vector<ImageMetadata::Keywordlist>;
+
   /** Band-specific metadatas */
   using ImageMetadataBandsType = std::vector<ImageMetadataBase>;
   ImageMetadataBandsType Bands;
@@ -262,6 +266,25 @@ public:
 
   /** if all bands share the same value of a key, put it at top level */
   void compact();
+
+  /** Export the Metadata as a list of KeywordList
+   *  The first KeywordList contains the metadata common to all the bands.
+   *  The following KeywordList contain the metadata of the bands.
+   *    */
+  void ToKeywordlists(KeywordlistVector&) const;
+
+  /** Export the bands of the Metadata as a list of KeywordList
+   *  Each KeywordList contain the metadata of a band.
+   *    */
+  void ToBandKeywordlists(KeywordlistVector&) const;
+
+  /** Import metadata from a list of keywordlist (will skip
+   *  MDGeom::SensorGeometry).
+   *  The first KeywordList contains the metadata common to all the bands.
+   *  The following KeywordList contain the metadata of the bands.
+   *  Returns True if all keywords were
+   *  parsed correctly */
+  bool FromKeywordlists(const KeywordlistVector&);
 };
 
 extern OTBMetadata_EXPORT std::ostream& operator<<(std::ostream& os, const otb::ImageMetadataBase& imd);
diff --git a/Modules/Core/Metadata/include/otbMetaDataKey.h b/Modules/Core/Metadata/include/otbMetaDataKey.h
index d725e6b3fb21e56952f26feb72a5c84efefec93f..7caa6f54ac1cfe75704bb481c8c16c35d634a3ea 100644
--- a/Modules/Core/Metadata/include/otbMetaDataKey.h
+++ b/Modules/Core/Metadata/include/otbMetaDataKey.h
@@ -269,7 +269,8 @@ inline boost::bimap<T, std::string> bimapGenerator(std::map<T, std::string> inMa
   return bm;
 }
 
-extern OTBMetadata_EXPORT std::map<MDGeom, std::string> MDGeomNames;
+typedef boost::bimap<MDGeom, std::string> MDGeomBmType;
+extern OTBMetadata_EXPORT MDGeomBmType MDGeomNames;
 
 typedef boost::bimap<MDNum, std::string> MDNumBmType;
 extern OTBMetadata_EXPORT MDNumBmType MDNumNames;
diff --git a/Modules/Core/Metadata/src/otbImageMetadata.cxx b/Modules/Core/Metadata/src/otbImageMetadata.cxx
index e6118f38bf23f2960928b48ed912f34ce950957f..ed37bde1177570b193fb68a6109dd8468af6ac8b 100644
--- a/Modules/Core/Metadata/src/otbImageMetadata.cxx
+++ b/Modules/Core/Metadata/src/otbImageMetadata.cxx
@@ -237,9 +237,10 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const
     oss.str("");
     if (kv.first == MDGeom::RPC)
     {
-//      Projection::RPCParam rpcStruct = boost::any_cast<Projection::RPCParam>(kv.second);
-//      cast_string = rpcStruct.ToJSON();
       oss << std::string("<RPCParam>");
+      // TODO: replace this std::string by a correct serialization of the RPCParam when implemented
+      // Projection::RPCParam rpcStruct = boost::any_cast<Projection::RPCParam>(kv.second);
+      // cast_string << rpcStruct;
     }
     else if (kv.first == MDGeom::ProjectionEPSG)
     {
@@ -247,9 +248,10 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const
     }
     else if (kv.first == MDGeom::GCP)
     {
-//      Projection::GCPParam gcpStruct = boost::any_cast<Projection::GCPParam>(kv.second);
-//      cast_string = gcpStruct.ToJSON();
       oss << std::string("<GCPParam>");
+      // TODO: replace this std::string by a correct serialization of the GCPParam when implemented
+      // Projection::GCPParam gcpStruct = boost::any_cast<Projection::GCPParam>(kv.second);
+      // cast_string << gcpStruct;
     }
     // TODO : MDGeom::SensorGeometry (should be exported as "<typeinfo>" where typeinfo is boost::any::type().name()
     // TODO : MDGeom::SAR
@@ -258,8 +260,7 @@ void ImageMetadataBase::ToKeywordlist(Keywordlist& kwl) const
     {
       oss << boost::any_cast<std::string>(kv.second);
     }
-    kwl.emplace(MetaData::MDGeomNames[kv.first], oss.str());
-
+    kwl.emplace(MetaData::MDGeomNames.left.at(kv.first), oss.str());
   }
   // Converting the StringKeys
   for (const auto& kv : StringKeys)
@@ -328,8 +329,37 @@ bool ImageMetadataBase::FromKeywordlist(const Keywordlist& kwl)
   // search iterators
   for (const auto& kv : kwl)
   {
-  // TODO Converting the GeomKeys
-  // skip MDGeom::SensorGeometry (they will be decoded by future SensorModelFactory)
+  // Converting the GeomKeys
+    auto geomKey = MetaData::MDGeomNames.right.find(kv.first);
+    if (geomKey != MetaData::MDGeomNames.right.end())
+    {
+      if(geomKey->second == MDGeom::RPC)
+      {
+        Projection::RPCParam rpcParam;
+        // TODO: Uncomment when deserialization is implemented
+        //kv.second >> rpcParam;
+        this->Add(geomKey->second, rpcParam);
+      }
+      else if (geomKey->second ==  MDGeom::ProjectionEPSG)
+      {
+        this->Add(geomKey->second, Utils::LexicalCast<int>(kv.second.c_str(), "Keywordlist.second.c_str()"));
+      }
+      else if (geomKey->second ==  MDGeom::GCP)
+      {
+        Projection::GCPParam gcpParam;
+        // TODO: Uncomment when deserialization is implemented
+        //kv.second >> gcpParam;
+        this->Add(geomKey->second, gcpParam);
+      }
+      // TODO : MDGeom::SAR
+      // TODO : MDGeom::Adjustment
+      // skip MDGeom::SensorGeometry (they will be decoded by future SensorModelFactory)
+      else
+      {
+        this->Add(geomKey->second, kv.second);
+      }
+      continue;
+    }
   // Converting the StringKeys
     auto strKey = MetaData::MDStrNames.right.find(kv.first);
     if (strKey != MetaData::MDStrNames.right.end())
@@ -373,11 +403,13 @@ bool ImageMetadataBase::FromKeywordlist(const Keywordlist& kwl)
     }
   // Converting the ExtraKeys
     std::string prefix("Extra.");
-    if (kv.first.compare(0, prefix.size(), prefix))
+    if (kv.first.compare(0, prefix.size(), prefix) == 0)
     {
       this->Add(kv.first.substr(prefix.size()), kv.second);
       continue;
     }
+    otbLogMacro(Warning, << "The metadata named '" << kv.first << "' with value '" << kv.second << "' was not parsed.")
+    all_parsed = false;
   }
   return all_parsed;
 }
@@ -432,6 +464,40 @@ void ImageMetadata::compact()
   // TODO
 }
 
+void ImageMetadata::ToKeywordlists(KeywordlistVector& kwlVect) const
+{
+  Keywordlist kwl;
+  this->ToKeywordlist(kwl);
+  kwlVect.push_back(kwl);
+  this->ToBandKeywordlists(kwlVect);
+}
+
+void ImageMetadata::ToBandKeywordlists(KeywordlistVector& kwlVect) const
+{
+  Keywordlist kwl;
+  for (const auto& band: this->Bands)
+  {
+    band.ToKeywordlist(kwl);
+    kwlVect.push_back(kwl);
+  }
+}
+
+bool ImageMetadata::FromKeywordlists(const KeywordlistVector& kwlVect)
+{
+  bool all_parsed = true;
+  auto kwlIt = kwlVect.cbegin();
+  all_parsed = this->FromKeywordlist(*kwlIt) && all_parsed;
+  ++kwlIt;
+  while (kwlIt != kwlVect.cend())
+  {
+    ImageMetadataBase imb;
+    all_parsed = imb.FromKeywordlist(*kwlIt) && all_parsed;
+    this->Bands.push_back(imb);
+    ++kwlIt;
+  }
+  return all_parsed;
+}
+
 // printing
 std::ostream& operator<<(std::ostream& os, const otb::ImageMetadataBase& imd)
 {
diff --git a/Modules/Core/Metadata/src/otbMetaDataKey.cxx b/Modules/Core/Metadata/src/otbMetaDataKey.cxx
index 324c816f3c375ffe7aeb3f9d70bec86e30160996..972d3ad8fe22e98ac95c39204a76deda20b357e2 100644
--- a/Modules/Core/Metadata/src/otbMetaDataKey.cxx
+++ b/Modules/Core/Metadata/src/otbMetaDataKey.cxx
@@ -262,9 +262,9 @@ void LUT<VDim>::FromString(std::string str)
   for (std::string line : lines)
   {
     boost::split(parts, line, [](char c){return c == '=';});
-    if (std::stoi(parts[0].substr(3, 1)) != VDim)
+    boost::trim(parts[1]);
+    if (Utils::LexicalCast<int>(parts[0].substr(3, 1), "VDim") != VDim)
       throw std::invalid_argument("Wrong LUT dimension");
-    std::string element = parts[0].substr(6, 5);
     if(parts[0].substr(6, 5) == "ARRAY")
     // this->Array
     {
@@ -276,14 +276,14 @@ void LUT<VDim>::FromString(std::string str)
     }
     else
     {
-      unsigned int dim = std::stoi(parts[0].substr(9, 1));
+      unsigned int dim = Utils::LexicalCast<int>(parts[0].substr(9, 1), "parts[0].substr(9, 1)");
       if (dim > VDim)
         throw std::invalid_argument("LUT dimension higher than expected");
-      element = parts[0].substr(11);
+      std::string element = parts[0].substr(11);
       if (element == "SIZE ")
       // this->Axis[dim].Size
       {
-        Axis[dim].Size = std::stoi(parts[1]);
+        Axis[dim].Size = Utils::LexicalCast<int>(parts[1], "Axis[dim].Size");
       }
       else if (element == "VALUES ")
       // this->Axis[dim].Values
@@ -294,15 +294,15 @@ void LUT<VDim>::FromString(std::string str)
         std::transform(str_array.begin(), str_array.end(), back_inserter(Axis[dim].Values),
                         [](std::string const& val) {return std::stod(val);});
       }
-      else if (element == "ORIGIN")
+      else if (element == "ORIGIN ")
       // this->Axis[dim].Origin
       {
-        Axis[dim].Origin = std::stoi(parts[1]);
+        Axis[dim].Origin = Utils::LexicalCast<double>(parts[1], "Axis[dim].Origin");
       }
-      else if (element == "SPACING")
+      else if (element == "SPACING ")
       // this->Axis[dim].Spacing
       {
-        Axis[dim].Spacing = std::stoi(parts[1]);
+        Axis[dim].Spacing = Utils::LexicalCast<double>(parts[1], "Axis[dim].Spacing");
       }
     }
   }
@@ -370,7 +370,7 @@ MDL1DBmType MDL1DNames = bimapGenerator<MDL1D>(std::map<MDL1D, std::string> {
 
 MDL2DBmType MDL2DNames = bimapGenerator<MDL2D>(std::map<MDL2D, std::string> {});
 
-std::map<MDGeom, std::string> MDGeomNames = {
+MDGeomBmType MDGeomNames = bimapGenerator<MDGeom>(std::map<MDGeom, std::string> {
   {MDGeom::ProjectionWKT,  "ProjectionWKT"},
   {MDGeom::ProjectionEPSG, "ProjectionEPSG"},
   {MDGeom::ProjectionProj, "ProjectionProj"},
@@ -379,7 +379,7 @@ std::map<MDGeom, std::string> MDGeomNames = {
   {MDGeom::SensorGeometry, "SensorGeometry"},
   {MDGeom::GCP,            "GCP"},
   {MDGeom::Adjustment,     "Adjustment"}
-};
+});
 
 } // end namespace MetaData
 
diff --git a/Modules/Core/Metadata/test/otbImageMetadataTest.cxx b/Modules/Core/Metadata/test/otbImageMetadataTest.cxx
index 8aa8cbb147110014025a1f1ad2139c760ece22be..09a1c0981fca80b46655dbabe8fb4270d89aee89 100644
--- a/Modules/Core/Metadata/test/otbImageMetadataTest.cxx
+++ b/Modules/Core/Metadata/test/otbImageMetadataTest.cxx
@@ -200,8 +200,8 @@ int otbImageMetadataTest(int argc, char* argv[])
   md4.Add(MDGeom::GCP, gcpStruct);
   MetaData::LUT1D lut1d;
   lut1d.Axis[0].Size = 3;
-  lut1d.Axis[0].Origin = 0;
-  lut1d.Axis[0].Spacing = 1;
+  lut1d.Axis[0].Origin = 0.;
+  lut1d.Axis[0].Spacing = 1.;
   std::vector<double>array({1.0, 2.0, 3.0});
   lut1d.Array = {1.0, 2.0, 3.0};
   md4.Add(MDL1D::SpectralSensitivity, lut1d);
@@ -210,6 +210,12 @@ int otbImageMetadataTest(int argc, char* argv[])
   md3.append(md4);
   outfile << "md3_append: "<< md3 << "\n";
 
+  ImageMetadata::KeywordlistVector kwlVect;
+  md3.ToKeywordlists(kwlVect);
+  ImageMetadata md5;
+  md5.FromKeywordlists(kwlVect);
+  outfile << "md5: "<< md5 << "\n";
+
   outfile.close();
   return EXIT_SUCCESS;
 }