INTERVIEW JOB CODING TASKS – XML file manipulation5 min read

It’s been a while, once again, since my last post. But due to a much of stuff, I’m doing in the background, I guarantee you, it’s going to be worth it! Bunch of changes is going to be presented to you very soon as well as new content, in a much more professional way.

Anyway, the topic for today is in the category of INTERVIEW QUESTIONS / TASKS, so let’s get rolling.

The task

Create a console application that is going to read a specific cell value from an XML based on our test schema (XSD) according to our console input.

I.e. if you type -1 -1 in our console, the application will exit. If you input values 1 3 the app will read XML value from row 1 and cell 3. Input values are digits only, all values must be of type string and XML is based on our TestSchema.XSD file.

Test XML

So we have a structured XML like this one:

<?xml version="1.0" encoding="utf-8" ?> 

<sheet> 

  <row> 

    <cell>Some string one</cell> 

    <cell>Random string cell two</cell> 

  </row> 

  <row> 

    <cell>Some kind of a value we are looking for</cell> 

  </row> 

</sheet> 

Based on that we created our test schema XSD:

Test schema XSD

<?xml version="1.0" encoding="utf-8"?> 

<xs:schema id="TestSchema" 

    targetNamespace="http://tempuri.org/TestSchema.xsd" 

    elementFormDefault="qualified" 

    xmlns="http://tempuri.org/TestSchema.xsd" 

    xmlns:mstns="http://tempuri.org/TestSchema.xsd" 

    xmlns:xs="http://www.w3.org/2001/XMLSchema" 

> 

  <xs:element name="sheet"> 

    <xs:complexType> 

      <xs:sequence> 

        <xs:element minOccurs="1" maxOccurs="1000" name="row"> 

          <xs:complexType> 

            <xs:sequence> 

              <xs:element minOccurs="1" maxOccurs="1000" name="cell" type="xs:string" /> 

            </xs:sequence> 

          </xs:complexType> 

        </xs:element> 

      </xs:sequence> 

    </xs:complexType> 

  </xs:element> 

</xs:schema> 

As seen in this XSD file, we defined our XML file to have rows and cells with a minimum of 1 and a maximum value of 1000. We also defined our cell element to be of a type string.

Next step is to define our schema model so we can easily manipulate our objects in code:

Schema model

namespace Task.Models 

{ 
    [XmlRoot(ElementName = "sheet")] 

    public class TestSchemaModel 

    { 
        [XmlElement(ElementName = "row")] 

        public List<Row> Rows { get; set; } 

    } 
  
    [XmlRoot(ElementName = "row")] 

    public class Row 

    { 
        [XmlElement(ElementName = "cell")] 

        public List<string> Cell { get; set; } 
    } 

} 

What we did here is we defined our XML as a model in our code. This way we are going to easily load XML in our application.

So basically we created a schema model which consists of a list of rows and we also defined a row model which consists of lists of cells that are of type string.

The next step is to create a custom class with methods that we are going to use to do XML file manipulation.

XML file manipulation

public static class XmlFileManipulation 

    { 
        public static TestSchemaModel GetXmlDeserializedData(string fileName) 

        { 

            TestSchemaModel schemaModel = default; 

 
            var doc = ValidateXDocument(fileName); 


            schemaModel = DeserializeFromXml<TestSchemaModel>(doc.ToString()); 

             

            return schemaModel; 

        } 

  

        private static XDocument ValidateXDocument(string fileName) 

        { 

            XDocument doc = default; 

            try 

            { 

                var path = new Uri(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().CodeBase) ?? throw new InvalidOperationException()).LocalPath; 

  
                var concatenatedStringPath = string.Concat(path, "\\TestSchema.xsd"); 

  

                XmlSchemaSet schema = new XmlSchemaSet(); 

                schema.Add("http://tempuri.org/TestSchema.xsd", concatenatedStringPath); 

  

                XmlReader rd = XmlReader.Create($"{path}\\{fileName}"); 

                doc = System.Xml.Linq.XDocument.Load(rd); 

  

                doc.Validate(schema, ValidationEventHandler); 

            } 

            catch (Exception ex) 

            { 

                Console.WriteLine($"Error during validating file: {ex.Message}"); 

            } 

  

            return doc; 

        } 

        public static void ValidationEventHandler(object sender, ValidationEventArgs e) 

        { 

            XmlSeverityType type = XmlSeverityType.Warning; 

  

            if (!Enum.TryParse<XmlSeverityType>("Error", out type)) return; 

  

            if (type == XmlSeverityType.Error) 

            { 

                Console.WriteLine(e.Message); 

            } 

        } 

  

        public static T DeserializeFromXml<T>(string xml) 

        { 

            T result = default; 

  

            try 

            { 

                XmlSerializer ser = new XmlSerializer(typeof(T)); 

                using (TextReader tr = new StringReader(xml)) 

                { 

                    result = (T) ser.Deserialize(tr); 

                } 

            } 

            catch (Exception ex) 

            { 

                Console.WriteLine($"Error while deserializing file: {ex.Message}"); 

            } 


            return result; 

        } 

    } 

What’s the purpose of this class? Well, obviously we are using this class for getting data out of our XML file. But before we retrieve our data we need to validate our XML file. That’s why we have the ValidateXDocument method which validates current XML against our XSD file. If our XML file is validated then we will retrieve our data, if not it will throw an error (ValidationEventHandler). The last method DeserializeFromXml is used to retrieve data.

And finally, here is the main code for our application:

Solution – our application

        static void Main(string[] args) 

        { 

            if (args.Length <= 0) 

            { 

                Console.WriteLine($"Please start program with the file name!"); 

                return; 

            } 

  

            var fileName = GetExistingFile(args); 

  

            var testSchemaModelData = XmlFileManipulation.GetXmlDeserializedData(fileName); 

  

            if (testSchemaModelData == null) 

            { 

                Console.WriteLine($"Cannot retrieve deserialized data."); 

                return; 

            } 

  

            var userInput = string.Empty; 

  

            Console.WriteLine("Please enter cell address:\n"); 

  

            while (userInput != "-1 -1") 

            { 

                userInput = Console.ReadLine(); 

  

                if (UserInputHasValues(userInput)) continue; 

  

                if (UserInputHasDigitsOnly(userInput)) continue; 

  

  

                if (UserInputHasTwoParamsOnly(userInput, out var rowCell)) continue; 

  

                var row = int.Parse(rowCell[0]); 

                var cell = int.Parse(rowCell[1]); 

  

                if (testSchemaModelData.Rows.ElementAtOrDefault(row) != null) 

                { 

                    var selectedRow = testSchemaModelData.Rows[row]; 

  

                    Console.WriteLine(selectedRow?.Cell.ElementAtOrDefault(cell) != null 

                        ? $"{selectedRow.Cell[cell]}" 

                        : "EMPTY"); 

                } 

                else 

                { 

                    Console.WriteLine("EMPTY"); 

                } 

            } 


            Environment.Exit(0); 

        } 

  

        private static string GetExistingFile(string[] args) 

        { 

            string fileName = args[0]; 

  

            while (!File.Exists(fileName)) 

            { 

                Console.WriteLine($"File {fileName} does not exist!\nTry adding new one:"); 

                fileName = Console.ReadLine(); 

            } 

  

            return fileName; 

        } 

  

        private static bool UserInputHasValues(string userInput) 

        { 

            if (!string.IsNullOrEmpty(userInput)) return false; 

            Console.WriteLine("Please enter TEXT values."); 

  

            return true; 

        } 

  

        private static bool UserInputHasDigitsOnly(string userInput) 

        { 

            var isDigit = userInput.Trim().Split(' ').All(s => s.All(char.IsDigit)); 

  

            if (isDigit) return false; 

  

            Console.WriteLine("Please enter DIGIT values."); 

  

            return true; 

        } 

  

        private static bool UserInputHasTwoParamsOnly(string userInput, out string[] rowCell) 

        { 

            rowCell = userInput.Trim().Split(' '); 

  

            if (rowCell.Length == 2) return false; 

  

            Console.WriteLine("Please enter only two(2) values."); 

 
            return true; 

        } 

    } 

Our code get’s XML data, check’s valid input, parses row and cell value and outputs it into the console.

That’s it, hope this helps you!

Cheers

(Visited 25 times, 1 visits today)

Leave a comment

Your email address will not be published. Required fields are marked *