diff --git a/Cargo.lock b/Cargo.lock
index 30a19f2c138dd8b04c14cf46afe9b9d60eccffd2..590c3180e05d5116d8eab681013719a9669d09ed 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -996,7 +996,7 @@ dependencies = [
 
 [[package]]
 name = "rusty-hektor"
-version = "1.1.0"
+version = "2.0.0"
 dependencies = [
  "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "calamine 0.15.5 (registry+https://github.com/rust-lang/crates.io-index)",
diff --git a/Cargo.toml b/Cargo.toml
index 167cab327ee8ead394b03ebf85889f5103530fd1..0fa99913d8f4f91714bca0f69af023f231a26814 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "rusty-hektor"
-version = "1.1.0"
+version = "2.0.0"
 authors = ["robinwilliam.hundt <robinwilliam.hundt@stud.uni-goettingen.de>"]
 license = "MIT OR Apache-2.0"
 description = "A tool to convert ILIAS exam output"
diff --git a/README.md b/README.md
index aede5e739f98a97c63adf1a8f70a30cb3489f7a7..01ecd98913d8a465792ee3117ae4f21b688ddddb 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,9 @@ If `-o` is omitted, the output is printed to stdout.
 
 If you call the program with the paths to an `.xml` and `.xls` export, both will be read and compared for inconsistencies.
 
+By default, the program parses the free text questions and submissions of the `.xml` input. Should you
+wish to skip those questions simply call the program with the `--skip-text` flag.
+
 ### Word List
 
 The anonymised names are generated by selecting random words from the EFF long list, intended for password generation.
diff --git a/src/main.rs b/src/main.rs
index e12583838884b4f8536c5d2bf9c90bac565c890c..1ae4d54a1087e136f54f8b22b1fc76413c929c64 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -2,6 +2,7 @@ use std::error::Error;
 use std::fs;
 use std::io::{self, Write};
 use std::path::PathBuf;
+use std::ops::Not;
 
 use chrono::{DateTime, Utc};
 use env_logger::{Builder, Env};
@@ -35,6 +36,10 @@ struct Opt {
     #[structopt(short, long = "non-interactive")]
     non_interactive: bool,
 
+    /// Skip free text questions
+    #[structopt(long = "skip-text")]
+    skip_text: bool,
+
     /// Where to store the anonymisation map file, if enabled
     #[structopt(short, long = "map-file", default_value = "anon.csv")]
     map_file_name: PathBuf,
@@ -58,6 +63,8 @@ fn main() -> Result<(), Box<dyn Error>> {
     }
     let opt = Opt::from_args();
 
+    let parse_text = opt.skip_text.not();
+
     let parsed_data: Result<Vec<ParsedData>, Box<dyn Error>> = opt
         .in_files
         .iter()
@@ -72,9 +79,9 @@ fn main() -> Result<(), Box<dyn Error>> {
                 )? {
                     std::process::exit(1)
                 }
-                Ok(XLS(XLSParser::parse(path)?))
+                Ok(XLS(XLSParser::parse(path, parse_text)?))
             } else if extension == "zip" {
-                Ok(XML(XMLParser::parse(path)?))
+                Ok(XML(XMLParser::parse(path, parse_text)?))
             } else {
                 Err(ParserError::new(format!(
                     "Unsupported filetype: {:?}",
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 95a6d7576b017b824f139832438487a55e17879d..e8ba93630fb51126dfdaedae3ce67b77228e735f 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -9,7 +9,7 @@ pub mod xml_parser;
 type Result<T> = std::result::Result<T, ParserError>;
 
 pub trait Parser {
-    fn parse<'a>(path: &Path) -> result::Result<Exam, Box<dyn Error>>;
+    fn parse<'a>(path: &Path, parse_text_questions: bool) -> result::Result<Exam, Box<dyn Error>>;
 }
 
 #[derive(Debug)]
diff --git a/src/parser/xls_parser.rs b/src/parser/xls_parser.rs
index d5ab17bcc572c93e16bb35abaf6600d761592805..dbf7965ce0f26a3147d0bc0f8b6777c13bad64ed 100644
--- a/src/parser/xls_parser.rs
+++ b/src/parser/xls_parser.rs
@@ -28,7 +28,12 @@ const OVERVIEW_SHEET_NAME: &'static str = "Testergebnisse";
 const SUBMISSION_IN_NEXT_ROW_MARKER: &'static str = "Quellcode Frage";
 
 impl Parser for XLSParser {
-    fn parse(path: &Path) -> Result<Exam, Box<dyn Error>> {
+    fn parse(path: &Path, parse_text_questions: bool) -> Result<Exam, Box<dyn Error>> {
+        if parse_text_questions {
+            Err(ParserError::new(
+                "XLS Parser is deprecated and doesn't support parsing of text questions",
+            ))?
+        }
         let mut workbook: Xls<_> = match open_workbook(path) {
             Ok(workbook) => workbook,
             Err(xls_err) => Err(ParserError::from(xls_err))?,
diff --git a/src/parser/xml_parser.rs b/src/parser/xml_parser.rs
index 5c8d250cbe959a9af71eba3a724a6b27128cac15..f4eb9a704a8f1e7aa042bbae044dcebd3fc5ae70 100644
--- a/src/parser/xml_parser.rs
+++ b/src/parser/xml_parser.rs
@@ -86,12 +86,17 @@ impl XMLFiles {
 pub struct XMLParser {}
 
 impl Parser for XMLParser {
-    fn parse(path: &Path) -> result::Result<Exam, Box<dyn Error>> {
+    fn parse(path: &Path, parse_text_questions: bool) -> result::Result<Exam, Box<dyn Error>> {
         let file = File::open(path)?;
         let mut archive = ZipArchive::new(file)?;
 
         let xml_files = XMLFiles::new(&mut archive)?;
-        let sub_types = extract_submission_types(&xml_files, &["assSourceCode"])?;
+        let allowed_question_types: &[&str] = if parse_text_questions {
+            &["assSourceCode", "TEXT QUESTION"]
+        } else {
+            &["assSourceCode"]
+        };
+        let sub_types = extract_submission_types(&xml_files, allowed_question_types)?;
         let students = extract_students(&xml_files, &sub_types)?;
 
         Ok(Exam {
diff --git a/src/submission_type.rs b/src/submission_type.rs
index 0c5472881b268afdaeffb254ec146817e8e28cd3..24b17f43b17215ad897804d15fd480448e460c05 100644
--- a/src/submission_type.rs
+++ b/src/submission_type.rs
@@ -79,11 +79,12 @@ pub enum ProgrammingLang {
     java,
     mipsasm,
     haskell,
+    text,
 }
 
 #[derive(Debug, Display)]
 #[display(
-    fmt = "Unparseable programming language: {}. Allowed: c, java, mipsasm, haskell",
+    fmt = "Unparseable programming language: {}. Allowed: c, java, mipsasm, haskell, text",
     input
 )]
 pub struct ParseProgrammingLangError {
@@ -101,6 +102,7 @@ impl FromStr for ProgrammingLang {
             "java" => ProgrammingLang::java,
             "mipsasm" => ProgrammingLang::mipsasm,
             "haskell" => ProgrammingLang::haskell,
+            "text" => ProgrammingLang::text,
             _ => Err(ParseProgrammingLangError {
                 input: s.to_owned(),
             })?,
diff --git a/tests/test_xls_parser.rs b/tests/test_xls_parser.rs
index 9d7375538f6dbc4f909ae4145126ec0aea9729e0..8fdf846f9bbb7d185af8fdfc633e4c6a4db49107 100644
--- a/tests/test_xls_parser.rs
+++ b/tests/test_xls_parser.rs
@@ -7,7 +7,7 @@ use std::path::Path;
 
 #[test]
 fn can_parse_xls_file() -> Result<(), Box<Error>> {
-    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"))?;
+    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"), false)?;
     let serializable = parsed.into_serializable()?;
 
     assert_eq!(3, serializable.students.len());
@@ -19,7 +19,7 @@ fn can_parse_xls_file() -> Result<(), Box<Error>> {
 
 #[test]
 fn parsed_xls_contains_coorect_submission_types() -> Result<(), Box<dyn Error>> {
-    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"))?;
+    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"), false)?;
     let serializable = parsed.into_serializable()?;
 
     let submission_type_names: HashSet<String> = serializable
@@ -47,7 +47,7 @@ fn parsed_xls_contains_coorect_submission_types() -> Result<(), Box<dyn Error>>
 
 #[test]
 fn parsed_xls_contains_correct_students() -> Result<(), Box<dyn Error>> {
-    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"))?;
+    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"), false)?;
     let serializable = parsed.into_serializable()?;
 
     let students: BTreeSet<StudentSerializable> = serializable
@@ -92,7 +92,7 @@ fn parsed_xls_contains_correct_students() -> Result<(), Box<dyn Error>> {
 fn correct_mapping_is_generated() -> Result<(), Box<dyn Error>> {
     use rusty_hektor::anonymizer;
 
-    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"))?;
+    let parsed = XLSParser::parse(Path::new("tests/test_export.xls"), false)?;
     let mut serializable = parsed.into_serializable()?;
 
     let map = anonymizer::replace_students(&mut serializable.students);
diff --git a/tests/test_xml_parser.rs b/tests/test_xml_parser.rs
index 0735cae924c1da4e17e98e51e0ced821e32963a2..3980767ef6640be7a271eb5a03b13b8b0d421ddf 100644
--- a/tests/test_xml_parser.rs
+++ b/tests/test_xml_parser.rs
@@ -5,7 +5,7 @@ use rusty_hektor::parser::{xml_parser::XMLParser, Parser};
 
 #[test]
 fn can_parse_zipped_xml_data() -> Result<(), Box<Error>> {
-    let parsed = XMLParser::parse(Path::new("tests/test.zip"))?;
+    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false)?;
     let serializable = parsed.into_serializable()?;
     assert_eq!(1, serializable.submission_types.len());
     assert_eq!(1, serializable.students.len());