Object Serialization and Deserialzation in c#

Previously I have written about how to serialize an object to XML. This post is an improved version of that post with better code and the addition of a deserialize example.
Required Namespace to be imported for serialization is System.Xml.Serialization.

This example is also available as a gist

public class Person
{
    public string Name {get; set;}
    public int Age {get;set;}
    public Address Address{get;set;}
}

public class Address
{
    public string Address1 {get;set;}
    public string Town {get;set;}
    public string PostCode{get;set;}
}

void Main()
{
    var p = new Person{
        Name = "John Jones",
        Age = 40,
        Address = new Address{
        Address1= "Daisy Meadow",
        Town="Chorville",
        PostCode = "CH1 1HC"
    }
};
	
XmlSerializer xmlSerializer = new XmlSerializer(p.GetType());
	
var xmlText = string.Empty;
using (TextWriter textWriter = new StringWriter()){
    xmlSerializer.Serialize(textWriter, p);
    xmlText = textWriter.ToString();
}

XmlReaderSettings settings = new XmlReaderSettings();
using(StringReader textReader = new StringReader(xmlText)) {
    using(XmlReader xmlReader = XmlReader.Create(textReader, settings)) {
         ((Person)xmlSerializer.Deserialize(xmlReader)).Dump();
        }
    }
}

Biztalk Recipe – Content Based Routing Example

This post is more about the technique rather than the scenario, so the solution is somewhat over engineered for what the scenario requires.
I generally create a separate Biztalk Server Project for Maps, Schemas, Orchestrations and Pipelines. Each project may then contain folders to group related content.

Environment:
Windows Server 2008 R2 Enterprise
Biztalk 2013 R1
Visual Studio 2012
MSSQL Server 2008 R2

Example Scenario
The HR department has moved the employee records to a new all singing all dancing record system as part of the HR phase 1 project.  The payroll system is still in the legacy system and is being integrated with the new HR system in phase 2.  Until then data relating to new employees must be exported from the new HR system and imported into the legacy payroll system.  For this to work efficiently an automated process is preferred.  So far the new HR system is configured to automatically create a file containing basic employee details each time a new employee is created.
Biztalk will form part of the process by moving the employee files to a specific folder, which folder the file is moved to will be controlled by a value contained in the XML file itself (Content Based Routing).

The XML file in this example is very simple. The value of the Department will determine which folder Biztalk routes the file to.
For the purpose of this example there are two files, one called Employee_HR.xml

<Employee>
    <ForeName>Percy</ForeName>
    <Surname>Philips</Surname>
    <DOB>1980-12-05</DOB>
    <Department>HR</Department>
</Employee>

and one called Employee_IT.xml

<Employee>
	<ForeName>John</ForeName>
	<Surname>Jones</Surname>
	<DOB>1980-12-05</DOB>
	<Department>IT</Department>
</Employee>

The new HR system places the files in the location E:\Test\Input\EmployeeDefinitions.
If you are following along copy the two XML definitions above in to files and save them on your computer so you can locate them later.

The requirements state that (we will look at this in greater detail later)

  • If the employee department is HR the file should be placed in the E:\Test\Output\Employee_HR folder.
  • If the employee department is IT the fiel should be placed in the E:\Test\Output\Employee_IT folder.

Solution Part 1 – Visual Studio
The first we need is the schema of the Employee Definition XML file.

  1. Open Visual Studio and open/create a project to hold the schemas we will be creating.
    If a new project was created for this example make sure the the Application Name on the Deployment tab of the project properties is set.
  2. Right-click the project in Solution Explorer and select Add then Add Generated Items… (If you prefer to use the main menu, on the Project menu, click Add Generated Items…)
  3. In the Add Generated Items dialog box, click Generate Schemas in the Templates section then click the Add button.
  4. In the Generate Schemas dialog select Well-Formed XML for the Document Type and for the Input File browse to and select one of the employee definition files you saved earlier.  It does not matter which file as both have the same schema.If you see Well-Formed XML (Not Loaded) in the drop-down list, Microsoft suggests you continue and select the appropriate document type anyway, and you will be guided through the process of installing the missing DLL. Then repeat these steps.

    For additional help see the post on Biztalk-Installing Schema Editor Extensions
  5. Click OK once a file is selected.

If the schema generated successfully there will now be a schema file with a name similar to the file selected (different extension .xsd).

biztalk-imported-employee-schema

For parts of Biztalk to be able to read the value of the Department we need to set the department as a promoted property.

  1. Expand the Schema tree so the full schema is visible.
  2. Right-click the Department and in the context menu select Promote then Quick Promotion. Biztalk will inform that a new file is being added to the project and that the Employee schema has changed.
  3. You should see a new schema named something like PropertySchema.xsd.

biztalk-promoted-department-property
As Visual Studio has created the schema file it will contain the property that was just promoted and may even contain an default Property1 property which can be deleted. This is all we need to do in Visual Studio. The project can now be built and deployed.

Solution Part 2 – Biztalk Server Administration Console
Looking back at the rules specifed above Biztalk needs to be able to receive a file from a location, process the file and save the file to a location.

Receiving a file from a location – Set up a Receive Port/Location

  1. Open Biztalk Server Administration
  2. Expand the console tree through the following nodes until you can see the application you want to create a receive port. In my case this is the Learning application.
    • BizTalk Server Administration
    • BizTalk Group
    • Applications
  3. Right-click Receive Ports, point to New, and then click One-way Receive Port.
  4. Give the receive port a Name.
    biztalk-receive-port
  5. In the left pane, click Receive Locations
  6. In the Receive Locations panel click New
  7. In the Receive Location Properties window specify a Name of the Receive location.
  8. Select XMLReceive from the Receive pipeline drop down.
    biztalk-receive-location
  9. In the Transport group, select FILE from the Type drop down and click Configure
  10. In the FILE Transport Properties dialog, on the General tab,  browse to the folder where the file will be picked up from.  In this scenario it is the folder the new HR system is configured to automatically create a file containing basic employee details each time a new employee is created.
    biztalk-receive-location-configure
  11. Click OK on each dialog to return to the Receive Ports list.

Sending a file to a location – Set up a Send Port

Setting up a Send port is very much like setting up a Receive Location.  First we set up where BizTalk will place the files for employees in the HR department.  For HR employees the folder will be E:\Test\Output\HR and for IT employees it will be E:\Test\Output\IT.  For you these location will probably be different.

  1. Right-click Send Ports, point to New, and then click Static One-way Send Port.
    biztalk-send port-hr
  2. In the Transport group, select FILE from the Type drop down and click Configure to open  the FILE Transport Properties dialog.
  3. In the FILE Transport Properties dialog, on the General tab,  browse to the Destination folder where the files for HR department will be placed, in my case E:\Test\Output\HR.
  4. Click OK.
  5. Select Filters on the left navigation.
  6. Create 2 filters
    biztalk-send-port-hr-filters

    • Property: BTS.ReceivePortName
      Operator: ==
      Value: ReceiveEmployeeDefinition
    • Property: Learning.Schemas.Employees.Department
      Operator: ==
      Value: HR
  7. Click OK.

To set up where BizTalk will place the files for employees in the IT department repeat the above steps.  I set the Destination folder on the FILE Transport Properties dialog to be E:\Test\Output\IT.  I set the filters for picking up the IT files as

  • Property: BTS.ReceivePortName
    Operator: ==
    Value: ReceiveEmployeeDefinition
  • Property: Learning.Schemas.Employees.Department
    Operator: ==
    Value: IT

That should be it.  Ensure the Receive Locations are Enabled and the Send Ports are started.

When a file is created in EmpoyeeDefinitions folder by the HR system Biztalk should pick it up and place a copy in either the HR or IT folder depending on the value of the department.

Things To Note
Are you familiar with the saying “There’s more than one way to skin a cat”, gross I know. Putting cats to one side there is certainly more than one way to create a schema or a promoted property.

Replacing The Text Of An Element In Multiple XML files

Recently a colleague needed a quick and easy way to replace the text in a specific element in over 1000 XML files. And he needed a solution NOW. There are probably a few ways this could be done without writing a line of code but I was intrigued be the coding challenge it provided.

I decided to use a console application would it would give me the quickest start. The main method of the application gets a list of all XML files in the same folder as the executable. For each file in the list it calls an XMLReplace method.

static void Main(string[] args)
{
    // Get the path to the directory
    var path = AppDomain.CurrentDomain.BaseDirectory;

    // Get all files in the path
    var files = Directory.GetFiles(path);

    foreach (var filePath in files.Where(p => Path.GetExtension(p) == ".xml"))
    {
        // for each xml file do the required actions
        Console.WriteLine(Path.GetFileName(filePath));
        XMLReplace(filePath);
    }
}

This is the XML schema I will be using in this example. The actual files I was given contained a much more complex schema.

<?xml version="1.0" encoding="utf-8" ?>
<contacts>
  <contact>
    <firstname>Charlie</firstname>
    <lastname>Wilks</lastname>
    <address>
      <street>Sea View</street>
      <city>Marley</city>
      <county>Manshire</county>
      <postcode>MA9 7BS</postcode>
      <country></country>
    </address>
  </contact>
</contacts>

The XMLReplace method contains the code that replaces the text of the XML element.

static void XMLReplace(string filePath)
{
    var xmlDoc = new XmlDocument();

    xmlDoc.Load(filePath);
    var elem = xmlDoc.SelectSingleNode("/contacts/contact/address/country");

    if (elem != null)
    {
        elem.InnerText = "UK";
        //xmlDoc.Save(filePath);
        XmlTextWriter wr = new XmlTextWriter(filePath, Encoding.UTF8);
        wr.Formatting = Formatting.None;
        xmlDoc.Save(wr);
    }
    else
    {
        Console.WriteLine("Element not found!");
    }
}

The first thing the method does is to load the XML document from the path parameter and search for the element. The SelectSingleNode function returns the first match even if the file contained multiple address elements. If a match is found the text is set to UK.
It is possible to save the file by calling the Save method of the XML document object passing in the path of the save location. But when the file is saved it will be formatted with line breaks. As we did not want formatting in the files I had, an XMLTextWriter was used. Setting the formatting to none saves the file with no formatting (line breaks or indentation etc) all text just comes out on a single line.

At this point I had a working solution which my colleague was very happy with. It works fine for files where an element only occurs once, but what about schemas where an element is repeated and all occurrences need to be set.
This can be accommodated with just a few changes to the XMLReplace method.
Firstly, instead of calling the SelectSingleNode function of the XmlDocument object the GetElementsByTagName function is can be called which returns all occurrences of an element. The GetElementsByTagName function accepts a string parameter which in this case is just the name of the element and not the whole hierarchical from the root to the required element. As there may be multiple elements found these can be iterated and the contents set to the desired string. Saving the XML back to the file is done with a XmlTextWriter as before.

static void XMLReplaceMultiple(string filePath)
{
    var xmlDoc = new XmlDocument();

    xmlDoc.Load(filePath);
    var elements = xmlDoc.GetElementsByTagName("country");

    for (int looper = 0; looper < elements.Count; looper++ )
    {
        elements.Item(looper).InnerText = "UK";
    }
    //xmlDoc.Save(filePath);
    XmlTextWriter wr = new XmlTextWriter(filePath, Encoding.UTF8);
    wr.Formatting = Formatting.None;
    xmlDoc.Save(wr);
}

That’s it for now, enjoy.

Massive Selection of Microsoft eBooks

There are millions of ebooks available for download on the internet and not all of them are available legally. However, Eric Ligman (Microsoft Senior Sales Excellence Manager) has compiled a massive list of freely, and legally, available Microsoft ebooks on his blog. They cover a large range of topics including, but not limited to

  • Windows 7
  • Windows 8
  • Windows 8.1
  • Office 2010
  • Office 2013
  • Office 365
  • SharePoint 2013
  • Dynamics CRM
  • PowerShell
  • Exchange Server
  • Lync 2013
  • System Center
  • Azure
  • Cloud
  • SQL Server
  • ASP.NET
  • C#

They are all available by permission of the copyright license/owner which may change in the future. Fot this reason I am not linking directly to any but to view the full list go here: largest-collection-of-free-microsoft-ebooks-ever

Click here for other posts relating to free books.

Serialise a class to XML in C#

This simple example shows how to serialise a class as XML. It is an easy way of getting the a full textual copy of the class structure, no matter how complicated the class is. In the past I have seen this used for logging values of a class instance.

First of all we need an class. I have made these up, with a few different types.

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DOB { get; set; }
    public Address Address { get; set; }
    public int NoOfDependents { get; set; }
    public double Height { get; set; }
}

public class Address
{
    public string Address1 { get; set; }
    public string Address2 { get; set; }
    public string Town { get; set; }
    public string City { get; set; }
    public string PostCode { get; set; }
}

And an instance of the class with some data. For this example I am actually creating a list of Person objects.

var listOfPeople = new List<Person>();

listOfPeople.Add(new Person
{
    DOB = DateTime.Today,
    FirstName = "Bernadette",
    LastName = "Williams-Harrison",
    NoOfDependents = 2,
    Height = 4.6,
    Address = new Address
    {
        Address1 = "2345 Dead End Street",
        Address2 = string.Empty,
        City = "Pembely",
        PostCode = "BB67 9PP",
        Town = "Little Stichins",
    }
});

listOfPeople.Add(new Person
{
    DOB = DateTime.Today,
    FirstName = "Andrew",
    LastName = "Carton",
    NoOfDependents = 1,
    Height = 6.0,
    Address = new Address
    {
        Address1 = "11 Park View",
        Address2 = "Chester House",
        City = string.Empty,
        PostCode = "CM1 4BY",
        Town = "Mordon",
    }
});

And the final part is the code to perform the sreialisation.

var xmlSerializer = new XmlSerializer(listOfPeople.GetType());
var textWriter = new StringWriter();
xmlSerializer.Serialize(textWriter, listOfPeople);

var result = textWriter.ToString();

The result variable will be of string type, containing XML. This is what it looks like

<?xml version="1.0" encoding="utf-16"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Person>
    <FirstName>Bernadette</FirstName>
    <LastName>Williams-Harrison</LastName>
    <DOB>2014-05-14T00:00:00+01:00</DOB>
    <Address>
      <Address1>2345 Dead End Street</Address1>
      <Address2 />
      <Town>Little Stichins</Town>
      <City>Pembely</City>
      <PostCode>BB67 9PP</PostCode>
    </Address>
    <NoOfDependents>2</NoOfDependents>
    <Height>4.6</Height>
  </Person>
  <Person>
    <FirstName>Andrew</FirstName>
    <LastName>Carton</LastName>
    <DOB>2014-05-14T00:00:00+01:00</DOB>
    <Address>
      <Address1>11 Park View</Address1>
      <Address2>Chester House</Address2>
      <Town>Mordon</Town>
      <City />
      <PostCode>CM1 4BY</PostCode>
    </Address>
    <NoOfDependents>1</NoOfDependents>
    <Height>6</Height>
  </Person>
</ArrayOfPerson>

One thing to note is the resulting text may contain formatting characters, such as \r, \n, \” or others.  Do with them what you will.