diff --git a/Code/Mantid/MantidPlot/CMakeLists.txt b/Code/Mantid/MantidPlot/CMakeLists.txt
index 8a3b45f4a3d068da13488359f155d3c27d09bd86..0b3329a562454ed87dafa0434c2e70d5fe01af70 100644
--- a/Code/Mantid/MantidPlot/CMakeLists.txt
+++ b/Code/Mantid/MantidPlot/CMakeLists.txt
@@ -129,6 +129,7 @@ set ( QTIPLOT_SRCS src/ApplicationWindow.cpp
                    src/TiledWindow.cpp
                    src/TitlePicker.cpp
                    src/TranslateCurveTool.cpp
+                   src/TSVSerialiser.cpp
                    src/UserFunction.cpp
                    src/VectorCurve.cpp
                    src/origin/OPJFile.cpp
@@ -372,6 +373,7 @@ set ( QTIPLOT_HDRS src/ApplicationWindow.h
                    src/TiledWindow.h
                    src/TitlePicker.h
                    src/TranslateCurveTool.h
+                   src/TSVSerialiser.h
                    src/UserFunction.h
                    src/VectorCurve.h
                    src/analysis/fft2D.h
diff --git a/Code/Mantid/MantidPlot/src/TSVSerialiser.cpp b/Code/Mantid/MantidPlot/src/TSVSerialiser.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ca8d736ea623c887c82f584ce3f900adb3e3f833
--- /dev/null
+++ b/Code/Mantid/MantidPlot/src/TSVSerialiser.cpp
@@ -0,0 +1,234 @@
+#include "TSVSerialiser.h"
+
+#include "MantidKernel/Logger.h"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/regex.hpp>
+#include <sstream>
+
+namespace
+{
+  Mantid::Kernel::Logger g_log("TSVSerialiser");
+}
+
+TSVSerialiser::TSVSerialiser() : m_curIndex(0)
+{
+}
+
+TSVSerialiser::TSVSerialiser(std::string lines)
+{
+  parseLines(lines);
+}
+
+void TSVSerialiser::parseLines(std::string lines)
+{
+  std::vector<std::string> lineVec;
+  boost::split(lineVec, lines, boost::is_any_of("\n"));
+
+  //Clear out any old data.
+  m_lines.clear();
+  m_sections.clear();
+
+  boost::regex valueLineRegex("([a-zA-Z0-9]+)\\b.*");
+  boost::regex closedSectionRegex("<([a-zA-Z0-9]+)>(.*)</\\1>");
+  boost::regex openSectionRegex("<([a-zA-Z0-9]+)>(.*)");
+
+  for(auto lineIt = lineVec.begin(); lineIt != lineVec.end(); ++lineIt)
+  {
+    std::string line = *lineIt;
+
+    if(line.length() == 0)
+      continue;
+
+    //Stores matched sections of a regex
+    boost::smatch matches;
+
+    //Check if this is a value line
+    if(boost::regex_match(line, matches, valueLineRegex))
+    {
+      std::string name = matches[1].str();
+
+      m_lines[name].push_back(line);
+
+      g_log.information() << "found value line with name '" << name << "'" << std::endl;
+      continue;
+    }
+
+    //Look for lines which open and close a section in one line: <section>data</section>
+    if(boost::regex_match(line, matches, closedSectionRegex))
+    {
+      std::string name = matches[1].str();
+      std::string contents = matches[2].str();
+
+      m_sections[name].push_back(contents);
+
+      g_log.information() << "found closed section '" << name << "' with contents='" << contents << "'" << std::endl;
+      continue;
+    }
+
+    //Check if this is the start of a multiline section, if so, consume the whole section.
+    if(boost::regex_match(line, matches, openSectionRegex))
+    {
+      std::stringstream sectionSS;
+
+      std::string name = matches[1].str();
+      std::string firstLine = matches[2].str();
+
+      //firstLine exists because of a legacy edgecase: the <folder> section keeps values on the same line as
+      //the opening tag, so we have to be able to read that.
+      if(firstLine.length() > 0)
+        sectionSS << firstLine << "\n";
+
+      std::stringstream openSS;
+      openSS << "<" << name << ">.*";
+      boost::regex openRegex(openSS.str());
+
+      std::stringstream closeSS;
+      closeSS << "</" << name << ">";
+      boost::regex closeRegex(closeSS.str());
+
+      //Next line, to begin parsing
+      lineIt++;
+
+      //Search for opening and closing tags, counting depth and building the section string.
+      for(int depth = 1; depth > 0 && lineIt != lineVec.end(); ++lineIt)
+      {
+        line = *lineIt;
+        //Are we going down?
+        if(boost::regex_match(line, openRegex))
+        {
+          depth++;
+        } else if(boost::regex_match(line, closeRegex))
+        {
+          depth--;
+        }
+
+        if(depth > 0)
+          sectionSS << line << "\n";
+      }
+
+      //Back to start of next line;
+      lineIt--;
+
+      std::string sectionStr = sectionSS.str();
+
+      //We drop the last character because it's a spare newline
+      if(sectionStr.size() > 0)
+        sectionStr.resize(sectionStr.size() - 1);
+
+      m_sections[name].push_back(sectionStr);
+
+      g_log.information() << "read <" << name << ">:\n---------------------------\n" << sectionSS.str() << "----------------------------" << std::endl;
+      continue;
+    }
+
+    //If we've made it here then we don't know what kind of line this is.
+    g_log.error() << "Unable to identify line in TSVSerialiser::parseLines(): '" << line << "'" << std::endl;
+  }
+}
+
+bool TSVSerialiser::hasLine(std::string name) const
+{
+  return ( m_lines.find(name) != m_lines.end() );
+}
+
+bool TSVSerialiser::hasSection(std::string name) const
+{
+  return ( m_sections.find(name) != m_sections.end() );
+}
+
+std::vector<std::string> TSVSerialiser::values(std::string name, size_t i) const
+{
+  //Select correct line with lineAsString, parse it, then return values
+  std::vector<std::string> ret;
+
+  std::string line = lineAsString(name, i);
+  boost::split(ret, line, boost::is_any_of("\t"));
+
+  return ret;
+}
+
+std::vector<std::string> TSVSerialiser::sections(std::string name) const
+{
+  if(!hasSection(name))
+    return std::vector<std::string>();
+
+  return m_sections.at(name);
+}
+
+std::string TSVSerialiser::lineAsString(const std::string name, const size_t i) const
+{
+  if(!hasLine(name))
+    return "";
+
+  auto lines = m_lines.at(name);
+
+  return lines[i];
+}
+
+bool TSVSerialiser::selectLine(std::string name, const size_t i)
+{
+  if(!hasLine(name))
+    return false;
+
+  if(i >= m_lines[name].size())
+    return false;
+
+  m_curValues = values(name, i);
+  m_curIndex = 1; //1 because we want to start on the values, not the name
+  return true;
+}
+
+int TSVSerialiser::asInt(const size_t i) const
+{
+  if(i >= m_curValues.size())
+    return 0;
+
+  std::string valStr = m_curValues.at(i);
+
+  std::stringstream valSS(valStr);
+  int ret;
+  valSS >> ret;
+
+  return ret;
+}
+
+double TSVSerialiser::asDouble(const size_t i) const
+{
+  if(i >= m_curValues.size())
+    return 0.00;
+
+  std::string valStr = m_curValues.at(i);
+
+  std::stringstream valSS(valStr);
+  double ret;
+  valSS >> ret;
+
+  return ret;
+}
+
+std::string TSVSerialiser::asString(const size_t i) const
+{
+  if(i >= m_curValues.size())
+    return "";
+
+  return m_curValues.at(i);
+}
+
+TSVSerialiser& TSVSerialiser::operator>>(int& val)
+{
+  val = asInt(m_curIndex++);
+  return *this;
+}
+
+TSVSerialiser& TSVSerialiser::operator>>(double& val)
+{
+  val = asDouble(m_curIndex++);
+  return *this;
+}
+
+TSVSerialiser& TSVSerialiser::operator>>(std::string& val)
+{
+  val = asString(m_curIndex++);
+  return *this;
+}
diff --git a/Code/Mantid/MantidPlot/src/TSVSerialiser.h b/Code/Mantid/MantidPlot/src/TSVSerialiser.h
new file mode 100644
index 0000000000000000000000000000000000000000..332959d40f89ad7d8e924d34f19b1e76ef90fa2c
--- /dev/null
+++ b/Code/Mantid/MantidPlot/src/TSVSerialiser.h
@@ -0,0 +1,68 @@
+#ifndef MANTID_TSVSERIALISER_H_
+#define MANTID_TSVSERIALISER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+/** Parses the formatting used in MantidPlot project files
+
+  @author Harry Jeffery, ISIS, RAL
+  @date 23/07/2014
+
+  Copyright &copy; 2007-2014 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
+
+  This file is part of Mantid.
+
+  Mantid is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  Mantid is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+  File change history is stored at: <https://github.com/mantidproject/mantid>
+*/
+class TSVSerialiser
+{
+public:
+
+  TSVSerialiser();
+
+  TSVSerialiser(std::string lines);
+
+  void parseLines(std::string lines);
+
+  bool hasLine(const std::string name) const;
+  bool hasSection(const std::string name) const;
+
+  std::vector<std::string> values(const std::string name, const size_t i = 0) const;
+  std::vector<std::string> sections(const std::string name) const;
+
+  std::string lineAsString(const std::string name, const size_t i = 0) const;
+
+  bool selectLine(const std::string name, const size_t i = 0);
+
+  int         asInt(const size_t i) const;
+  double      asDouble(const size_t i) const;
+  std::string asString(const size_t i) const;
+
+  TSVSerialiser& operator>>(int& val);
+  TSVSerialiser& operator>>(double& val);
+  TSVSerialiser& operator>>(std::string& val);
+
+private:
+  std::map<std::string,std::vector<std::string> > m_sections;
+  std::map<std::string,std::vector<std::string> > m_lines;
+
+  std::vector<std::string> m_curValues;
+  int m_curIndex;
+};
+
+#endif