diff --git a/dumux/io/container.hh b/dumux/io/container.hh
index 7799653c783a7de070492bf60931db55b1a2fedb..a7dea7dd35ff3ce69db7049add933b1c71d20f99 100644
--- a/dumux/io/container.hh
+++ b/dumux/io/container.hh
@@ -33,6 +33,8 @@
 #include <fstream>
 #include <iterator>
 
+#include <dune/common/exceptions.hh>
+
 namespace Dumux {
 
 /*!
@@ -55,21 +57,36 @@ void writeContainerToFile(const Container& v,
     std::copy(v.begin(),v.end(), it);
 }
 
+/*!
+ * \brief Read an input stream into a container
+ * \param stream A standard input stream
+ * \tparam Container The container type requires begin(), end(), push_back() functions
+ *                   and Container::value_type requires operator>>.
+ */
+template<typename Container>
+Container readStreamToContainer(std::istream& stream)
+{
+    Container v;
+    std::istream_iterator<typename Container::value_type> it(stream);
+    std::copy(it, std::istream_iterator<typename Container::value_type>(), std::back_inserter(v));
+    return v;
+}
+
 /*!
  * \brief Read a simple text file into a container
  * \param filename The filename to write to
- * \tparam Container  The container type, requires begin(), end(), push_back() method
+ * \tparam Container The container type requires begin(), end(), push_back() functions
+ *                   and Container::value_type requires operator>>.
  *
  * usage: auto v = readFileToContainer<std::vector<double>>("myvector.txt");
  */
 template<typename Container>
 Container readFileToContainer(const std::string& filename)
 {
-    Container v;
     std::ifstream infile(filename, std::ios::in);
-    std::istream_iterator<typename Container::value_type> it(infile);
-    std::copy(it, std::istream_iterator<typename Container::value_type>(), std::back_inserter(v));
-    return v;
+    if (!infile)
+        DUNE_THROW(Dune::IOError, "Could not open file: " << filename);
+    return readStreamToContainer<Container>(infile);
 }
 
 } // end namespace Dumux
diff --git a/dumux/io/vtk/vtkreader.hh b/dumux/io/vtk/vtkreader.hh
index d9e71c42adb2cb418302ce5015cf30a0d0daee66..cf2f94bc8c5baa1eac5bb1bf7ba8351785357afe 100644
--- a/dumux/io/vtk/vtkreader.hh
+++ b/dumux/io/vtk/vtkreader.hh
@@ -36,9 +36,11 @@
 #include <dune/common/exceptions.hh>
 #include <dune/grid/common/capabilities.hh>
 #include <dune/grid/io/file/vtk/common.hh>
-#include <dumux/io/xml/tinyxml2.h>
 #include <dune/grid/common/gridfactory.hh>
 
+#include <dumux/io/container.hh>
+#include <dumux/io/xml/tinyxml2.h>
+
 namespace Dumux {
 
 /*!
@@ -479,11 +481,8 @@ private:
     template<class Container>
     Container parseDataArray_(const tinyxml2::XMLElement* dataArray) const
     {
-        Container data;
         std::stringstream dataStream(dataArray->GetText());
-        std::istream_iterator<typename Container::value_type> it(dataStream);
-        std::copy(it, std::istream_iterator<typename Container::value_type>(), std::back_inserter(data));
-        return data;
+        return readStreamToContainer<Container>(dataStream);
     }
 
     /*!
diff --git a/test/io/CMakeLists.txt b/test/io/CMakeLists.txt
index a6346665975c27a9b9545ba87728c25a8bef2747..b2d84016e92485ae8aa074c1d79f1de86172b6c6 100644
--- a/test/io/CMakeLists.txt
+++ b/test/io/CMakeLists.txt
@@ -1,5 +1,6 @@
+add_subdirectory(container)
 add_subdirectory(gnuplotinterface)
 add_subdirectory(gridmanager)
-add_subdirectory(container)
-add_subdirectory(vtk)
+add_subdirectory(inputdata)
 add_subdirectory(rasterimagereader)
+add_subdirectory(vtk)
diff --git a/test/io/inputdata/CMakeLists.txt b/test/io/inputdata/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ecf567d10832f99a1ae87a8ce813cdb2f0c6d037
--- /dev/null
+++ b/test/io/inputdata/CMakeLists.txt
@@ -0,0 +1,2 @@
+dune_symlink_to_source_files(FILES config.ini coordinates.txt mydata.xml numbers.txt)
+dumux_add_test(SOURCES test_io_data_input.cc LABELS unit io)
diff --git a/test/io/inputdata/config.ini b/test/io/inputdata/config.ini
new file mode 100644
index 0000000000000000000000000000000000000000..691d2cefa42baae1d636ca451d19c74739a3276e
--- /dev/null
+++ b/test/io/inputdata/config.ini
@@ -0,0 +1,6 @@
+[MyData]
+InjectionRate = 0.1 0.2 0.3 0.4
+BoundaryFlags = 3 4 5 6
+
+[Problem]
+Gravity = false
diff --git a/test/io/inputdata/coordinates.txt b/test/io/inputdata/coordinates.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4360a73c26fa9daf6d10d0c18a16e0f608859bac
--- /dev/null
+++ b/test/io/inputdata/coordinates.txt
@@ -0,0 +1,4 @@
+0.1 0.1 0.054
+0.2 0.1 0.054
+0.3 0.2 0.054
+0.4 0.1 0.454
diff --git a/test/io/inputdata/mydata.xml b/test/io/inputdata/mydata.xml
new file mode 100644
index 0000000000000000000000000000000000000000..64e9b2cff391d87b68c4ed2ba079fa7c2765bab1
--- /dev/null
+++ b/test/io/inputdata/mydata.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<!-- Simple custom data file -->
+<InputData>
+  <!-- Injection rates are in kg/s -->
+  <InjectionRates>
+  0.1 0.2 0.3 0.4
+  </InjectionRates>
+
+  <!-- BoundaryTypes: -1: no boundary, 0: Dirichlet, 1: Noflow, 2: Infiltration, 3: Outflow -->
+  <BoundaryTypes>
+  0 -1 -1 2
+  </BoundaryTypes>
+</InputData>
diff --git a/test/io/inputdata/numbers.txt b/test/io/inputdata/numbers.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c23c5da710efc48cf929a2358caa3f4a3a54d59e
--- /dev/null
+++ b/test/io/inputdata/numbers.txt
@@ -0,0 +1,4 @@
+0.1
+0.2
+0.3
+0.4
diff --git a/test/io/inputdata/test_io_data_input.cc b/test/io/inputdata/test_io_data_input.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0be56eb9c3c8193e662653d83e0a29c869fc32d5
--- /dev/null
+++ b/test/io/inputdata/test_io_data_input.cc
@@ -0,0 +1,112 @@
+// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+// vi: set et ts=4 sw=4 sts=4:
+/*****************************************************************************
+ *   See the file COPYING for full copying permissions.                      *
+ *                                                                           *
+ *   This program 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.                                     *
+ *                                                                           *
+ *   This program 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
+ * \brief Test and example of how to read custom user data in Dumux
+ */
+#include <config.h>
+
+#include <algorithm>
+#include <vector>
+#include <iostream>
+
+#include <dune/common/exceptions.hh>
+#include <dune/common/fvector.hh>
+#include <dune/common/parametertreeparser.hh>
+
+#include <dumux/io/container.hh>
+#include <dumux/io/xml/tinyxml2.h>
+
+////////////////////////
+// the main function
+////////////////////////
+int main()
+{
+    using namespace Dumux;
+
+    {
+        std::cout << "Reading a simple list of numbers into a vector\n";
+        std::cout << "-- Read numbers.txt: \n";
+
+        // the file extension ".txt" is arbitrary, works with any other extension or none
+        const auto numbers = readFileToContainer<std::vector<double>>("numbers.txt");
+
+        std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<double>(std::cout, ", "));
+        std::cout << "\n" << std::endl;
+    }
+
+    {
+        std::cout << "Reading a three-column list of numbers into a vector\n";
+        std::cout << "-- Read coordinates.txt: \n";
+
+        // the file extension ".txt" is arbitrary, works with any other extension or none
+        const auto coordinates = readFileToContainer<std::vector<Dune::FieldVector<double, 3>>>("coordinates.txt");
+
+        std::copy(coordinates.begin(), coordinates.end(), std::ostream_iterator<Dune::FieldVector<double, 3>>(std::cout, ", "));
+        std::cout << "\n" << std::endl;
+    }
+
+    {
+        std::cout << "Reading a key-value ini-style file into a Dune::ParameterTree\n";
+        std::cout << "-- Read config.ini: \n";
+
+        Dune::ParameterTree config;
+        // the file extension ".ini" is arbitrary, works with any other extension or none
+        Dune::ParameterTreeParser::readINITree("config.ini", config);
+
+        config.report();
+        std::cout << "\n";
+
+        std::cout << "-- Parsing MyData.InjectionRate into a vector: \n";
+
+        const auto injectionRates = config.get<std::vector<double>>("MyData.InjectionRate");
+
+        std::copy(injectionRates.begin(), injectionRates.end(), std::ostream_iterator<double>(std::cout, " "));
+        std::cout << "\n" << std::endl;
+    }
+
+    {
+        std::cout << "Reading a XML-formatted data using tiny xml\n";
+
+        tinyxml2::XMLDocument xmlData;
+        // the file extension ".xml" is arbitrary, works with any other extension or none
+        const auto returnCode = xmlData.LoadFile("mydata.xml");
+        if (returnCode != tinyxml2::XML_SUCCESS)
+            DUNE_THROW(Dune::IOError, "Couldn't open XML file.");
+        const tinyxml2::XMLElement* inputData = xmlData.FirstChildElement("InputData");
+
+        std::cout << "-- Read InjectionRates field: \n";
+
+        std::stringstream injectionData(inputData->FirstChildElement("InjectionRates")->GetText());
+        const auto injectionRates = readStreamToContainer<std::vector<double>>(injectionData);
+
+        std::copy(injectionRates.begin(), injectionRates.end(), std::ostream_iterator<double>(std::cout, " "));
+        std::cout << "\n";
+
+        std::cout << "-- Read BoundaryTypes field: \n";
+
+        std::stringstream boundaryData(inputData->FirstChildElement("BoundaryTypes")->GetText());
+        const auto boundaryTypes = readStreamToContainer<std::vector<int>>(boundaryData);
+
+        std::copy(boundaryTypes.begin(), boundaryTypes.end(), std::ostream_iterator<int>(std::cout, " "));
+        std::cout << "\n" << std::endl;
+    }
+
+    return 0;
+}