diff --git a/src/bin/rusty-hektor.rs b/src/bin/rusty-hektor.rs
index 47fa5a405f3170a9fcd25fee5871849f0bdcd0c7..e301991776950f99fe87789780ae44cf4d7b7741 100644
--- a/src/bin/rusty-hektor.rs
+++ b/src/bin/rusty-hektor.rs
@@ -10,7 +10,7 @@ use semver::Version;
 use serde_derive::Deserialize;
 use structopt::StructOpt;
 
-use rusty_hektor::exam::{Exam, ExamSerializable, merge_exams};
+use rusty_hektor::exam::{merge_exams, Exam, ExamSerializable};
 use rusty_hektor::module::Module;
 use rusty_hektor::parser::ipynb_parser::notebook::Notebook;
 use rusty_hektor::parser::xml_parser::XMLParser;
@@ -47,6 +47,10 @@ struct Opt {
     #[structopt(long = "skip-text")]
     skip_text: bool,
 
+    /// Parse cloze type questions
+    #[structopt(long = "parse-cloze")]
+    parse_cloze: bool,
+
     /// Disable latex rendering
     #[structopt(long = "no-latex")]
     no_latex: bool,
@@ -102,6 +106,7 @@ fn main() -> Result<(), Box<dyn Error>> {
     let opt = Opt::from_args();
 
     let parse_text = opt.skip_text.not();
+    let parse_cloze = opt.parse_cloze;
     let render_latex = opt.no_latex.not();
 
     let parsed_data: Result<Vec<Exam>, Box<dyn Error>> = opt
@@ -118,7 +123,12 @@ fn main() -> Result<(), Box<dyn Error>> {
                 );
                 std::process::exit(1)
             } else if extension == "zip" {
-                Ok(XMLParser::parse(path, parse_text, render_latex)?)
+                Ok(XMLParser::parse(
+                    path,
+                    parse_text,
+                    parse_cloze,
+                    render_latex,
+                )?)
             } else {
                 Err(ParserError::new(format!(
                     "Unsupported filetype: {:?}",
@@ -129,7 +139,7 @@ fn main() -> Result<(), Box<dyn Error>> {
         .collect();
 
     let parsed_data = parsed_data?;
-    let parsed_data= merge_exams(&parsed_data)?;
+    let parsed_data = merge_exams(&parsed_data)?;
 
     let mut serializable = parsed_data.into_serializable()?;
 
diff --git a/src/errors.rs b/src/errors.rs
index 53afd835456827b8562fe603d1461f3173bce44f..e0f208db1684b6e23c674eb853a1e3f6c1e04560 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -5,6 +5,7 @@ pub enum ValidationError {
     EmptyField(EmptyFieldError),
     InvalidStringLength(InvalidStringLengthError),
     InvalidMatNo(InvalidMatrNumber),
+    AttributeNotPresent(AttributeNotPresent),
 }
 
 impl fmt::Display for ValidationError {
@@ -13,6 +14,7 @@ impl fmt::Display for ValidationError {
             ValidationError::EmptyField(ref e) => e.fmt(f),
             ValidationError::InvalidMatNo(ref e) => e.fmt(f),
             ValidationError::InvalidStringLength(ref e) => e.fmt(f),
+            ValidationError::AttributeNotPresent(ref e) => e.fmt(f),
         }
     }
 }
@@ -107,3 +109,22 @@ impl fmt::Display for MergeError {
 }
 
 impl Error for MergeError {}
+
+#[derive(Debug)]
+pub struct AttributeNotPresent {
+    attribute: &'static str,
+}
+
+impl AttributeNotPresent {
+    pub fn new(attribute: &'static str) -> Self {
+        Self { attribute }
+    }
+}
+
+impl fmt::Display for AttributeNotPresent {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "attribute not found: \"{}\" ", self.attribute)
+    }
+}
+
+impl Error for AttributeNotPresent {}
diff --git a/src/exam.rs b/src/exam.rs
index 7266d61e4ce28e11e4fa67f1455db9a5cd7027b1..4e303a793c752de77eb1ed47e0006bc76779a1a3 100644
--- a/src/exam.rs
+++ b/src/exam.rs
@@ -1,15 +1,15 @@
+use std::collections::{BTreeMap};
 use std::collections::BTreeSet;
-use std::collections::{BTreeMap, HashSet};
 use std::error::Error;
 
-use crate::errors::{EmptyFieldError, MergeError};
+use itertools::Itertools;
+
+use crate::errors::{EmptyFieldError};
 use crate::meta_information::MetaInformation;
 use crate::module::Module;
 use crate::student::Student;
 use crate::student::StudentSerializable;
 use crate::submission_type::SubmissionType;
-use crate::yes_or_no;
-use itertools::Itertools;
 
 #[derive(Debug, Default)]
 pub struct Exam {
@@ -74,7 +74,7 @@ pub fn merge_exams(exams: &[Exam]) -> Result<Exam, Box<dyn Error>> {
     Ok(Exam {
         module: None,
         submission_types,
-        students
+        students,
     })
 }
 
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index e9dba4bef1948707e6df952f3e3a39c337bebba8..b9fa35c19a7cdc667e6060838b75a6d582499efd 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -10,6 +10,7 @@ pub trait Parser {
     fn parse<'a>(
         path: &Path,
         parse_text_questions: bool,
+        parse_cloze_question: bool,
         render_latex: bool,
     ) -> result::Result<Exam, Box<dyn Error>>;
 }
diff --git a/src/parser/xml_parser.rs b/src/parser/xml_parser.rs
index bac39269a79459fb2aa637a23e8088fd189e6805..0e4a0b238bdd89dce0189059608219367e1794c2 100644
--- a/src/parser/xml_parser.rs
+++ b/src/parser/xml_parser.rs
@@ -16,7 +16,8 @@ use zip::ZipArchive;
 use lazy_static::lazy_static;
 
 use crate::errors::{
-    EmptyFieldError, InvalidMatrNumber, InvalidStringLengthError, ValidationError,
+    AttributeNotPresent, EmptyFieldError, InvalidMatrNumber, InvalidStringLengthError,
+    ValidationError,
 };
 use crate::exam::Exam;
 use crate::parser::{Parser, ParserError};
@@ -89,18 +90,26 @@ impl Parser for XMLParser {
     fn parse(
         path: &Path,
         parse_text_questions: bool,
+        parse_cloze_question: bool,
         render_latex: 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 allowed_question_types: &[&str] = if parse_text_questions {
-            &["assSourceCode", "TEXT QUESTION"]
-        } else {
-            &["assSourceCode"]
+        let allowed_question_types: Vec<&str> = {
+            let mut allowed = vec!["assSourceCode"];
+            if parse_text_questions {
+                allowed.push("TEXT QUESTION");
+            }
+            if parse_cloze_question {
+                allowed.push("CLOZE QUESTION");
+            }
+            allowed
         };
-        let sub_types = extract_submission_types(&xml_files, allowed_question_types, render_latex)?;
+
+        let sub_types =
+            extract_submission_types(&xml_files, &allowed_question_types, render_latex)?;
         let students = extract_students(&xml_files, &sub_types)?;
 
         Ok(Exam {
@@ -195,9 +204,7 @@ fn extract_students(
         let element = item
             .element()
             .expect(&format!("No element for {:?} present", item));
-        let mut matr_nr = element
-            .attribute_value("matr_nr")
-            .expect("No matr_nr attr found");
+        let matr_nr = element.attribute_value("matr_nr");
         let active_id = element
             .attribute_value("active_id")
             .expect("No active_id attr found");
@@ -205,22 +212,25 @@ fn extract_students(
             .attribute_value("fullname")
             .expect("No fullname attr found");
 
-        match validate_mat_no(&matr_nr) {
-            Err(ValidationError::EmptyField(_)) => {
+        let matr_nr = match validate_mat_no(matr_nr) {
+            Err(ValidationError::EmptyField(_)) | Err(ValidationError::AttributeNotPresent(_)) => {
                 warn!(
                     "matr_nr for student {} is empty, falling back to active_id",
                     fullname
                 );
-                matr_nr = active_id;
+                active_id
             }
-            Err(e) => warn!(
-                "matr_no of {} has wrong format: {}\n{}",
-                fullname,
-                matr_nr,
-                e.description()
-            ),
-            _ => {}
-        }
+            Err(e) => {
+                warn!(
+                    "matr_no of {} has wrong format: {:?}\n Falling back to active_id\n{}",
+                    fullname,
+                    matr_nr,
+                    e.description()
+                );
+                active_id
+            }
+            Ok(()) => matr_nr.unwrap(),
+        };
 
         let submissions =
             extract_submissions_for_student(results_doc, active_id, submission_types)?;
@@ -238,7 +248,16 @@ fn extract_students(
     Ok(results)
 }
 
-fn validate_mat_no(mat_no: &str) -> Result<(), ValidationError> {
+fn validate_mat_no(mat_no: Option<&str>) -> Result<(), ValidationError> {
+    let mat_no = match mat_no {
+        Some(val) => val,
+        None => {
+            return Err(ValidationError::AttributeNotPresent(
+                AttributeNotPresent::new("matr_no"),
+            ))
+        }
+    };
+
     if mat_no.len() == 0 {
         Err(ValidationError::EmptyField(EmptyFieldError::new(
             "Unknown student",
@@ -314,18 +333,28 @@ fn extract_submission_for_student(
         .into_iter()
         .last();
 
-    let code = match oldest {
-        Some(node) => {
-            let elem = node
-                .element()
-                .expect(&format!("No element for {:?} present", node));
-            String::from_utf8(decode(
-                elem.attribute_value("value1")
-                    .expect("No value1 attr found, unable to parse code"),
-            )?)?
+    let result_el = oldest.map(|node| {
+        node.element()
+            .expect(&format!("No element for {:?} present", node))
+    });
+
+    let encoded_attr = result_el.map(|el| {
+        let mut val = el
+            .attribute_value("value1")
+            .expect("No value1 attr found, unable to parse code");
+        if val == "0" {
+            val = el
+                .attribute_value("value2")
+                .expect("value1 is 0 but value2 attr not found, unable to parse code");
         }
-        None => "".to_owned(),
+        val
+    });
+
+    let code = match encoded_attr {
+        Some(val) => String::from_utf8(decode(val)?)?,
+        None => "".to_string(),
     };
+
     let submission_type = sub_type.name.clone();
 
     Ok(Submission {
diff --git a/src/submission_type.rs b/src/submission_type.rs
index e5b36a3a1ccc60b99c29f5505a869c378df939e8..9b0e0568e9c867dbacc563a824c67c2572b008c7 100644
--- a/src/submission_type.rs
+++ b/src/submission_type.rs
@@ -67,7 +67,13 @@ impl SubmissionType {
             }
         }
         if self.programming_language.is_none() {
-            self.programming_language = Some(input("Programming language"));
+            self.programming_language = Some(input(
+                format!(
+                    "Programming language (available: {})",
+                    ProgrammingLang::list_available()
+                )
+                .as_str(),
+            ));
         }
 
         Ok(())
@@ -116,11 +122,18 @@ pub enum ProgrammingLang {
     haskell,
     python,
     plaintext,
+    markdown,
+}
+
+impl ProgrammingLang {
+    fn list_available() -> &'static str {
+        return "c, java, mipsasm, haskell, plaintext, markdown, python";
+    }
 }
 
 #[derive(Debug, Display)]
 #[display(
-    fmt = "Unparseable programming language: {}. Allowed: c, java, mipsasm, haskell, python, plaintext",
+    fmt = "Unparseable programming language: {}. Allowed: c, java, mipsasm, haskell, python, plaintext, markdown",
     input
 )]
 pub struct ParseProgrammingLangError {
@@ -139,6 +152,7 @@ impl FromStr for ProgrammingLang {
             "mipsasm" => ProgrammingLang::mipsasm,
             "haskell" => ProgrammingLang::haskell,
             "plaintext" => ProgrammingLang::plaintext,
+            "markdown" => ProgrammingLang::markdown,
             "python" => ProgrammingLang::python,
             _ => Err(ParseProgrammingLangError {
                 input: s.to_owned(),
diff --git a/tests/test_xml_parser.rs b/tests/test_xml_parser.rs
index 788edd68772ac18f24859bc668dedad2ae3a7ca8..a95626d0028fd7817d465742ccde969b157c3830 100644
--- a/tests/test_xml_parser.rs
+++ b/tests/test_xml_parser.rs
@@ -7,7 +7,7 @@ use std::collections::{BTreeSet, HashSet};
 
 #[test]
 fn can_parse_zipped_xml_data() -> Result<(), Box<dyn Error>> {
-    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, true)?;
+    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, false,true)?;
     let serializable = parsed.into_serializable()?;
     assert_eq!(1, serializable.submission_types.len());
     assert_eq!(1, serializable.students.len());
@@ -16,7 +16,7 @@ fn can_parse_zipped_xml_data() -> Result<(), Box<dyn Error>> {
 
 #[test]
 fn parsed_xml_contains_correct_submission_types() -> Result<(), Box<dyn Error>> {
-    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, true)?;
+    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, false, true)?;
     let serializable = parsed.into_serializable()?;
 
     let submission_type_names: HashSet<String> = serializable
@@ -37,7 +37,7 @@ fn parsed_xml_contains_correct_submission_types() -> Result<(), Box<dyn Error>>
 
 #[test]
 fn parsed_xls_contains_correct_students() -> Result<(), Box<dyn Error>> {
-    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, true)?;
+    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, false,true)?;
     let serializable = parsed.into_serializable()?;
 
     let students: BTreeSet<StudentSerializable> = serializable
@@ -68,7 +68,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 = XMLParser::parse(Path::new("tests/test.zip"), false, true)?;
+    let parsed = XMLParser::parse(Path::new("tests/test.zip"), false, false,true)?;
     let mut serializable = parsed.into_serializable()?;
 
     let map = anonymizer::replace_students(&mut serializable.students);