SohamC SohamC - 2 months ago 6
C# Question

Serialize specific properties of a class for TSV

I have a POCO of a following type

public class Person{
public Guid Id { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public int PinCode { get; set; }
public string Phone { get; set; }
}


I have a list of
Person
s which I want to convert to TSV(tab separated values). But I only want specific properties to be considered while creation of the TSV. In this case, I want only
Id
,
Name
and
Phone
.

The code I am using to convert to TSV is as below

public string Serialize(string separator, IEnumerable<Person> objectlist)
{
string[] fields = new[] { "Id", "Name", "Phone" };
StringBuilder tsvdata = new StringBuilder();

string header = string.Join(separator, fields);
tsvdata.AppendLine(header);
foreach (var o in objectlist)
tsvdata.AppendLine(o.Id + separator + o.Person + separator + o.Phone);

return tsvdata.ToString();
}


I do not want to statically mention/hard code the fields to consider as they may change in future. How can I achieve this?

Answer

Even if you are not using an external library for creating a TSV, you can still use attributes to mark special properties (e.g. those that you are left out). Then you can use reflection to check existing properties and attributes.

Here is a complete example:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;

namespace ConsoleApplication1
{
    public class Person
    {
        [DataMember]
        public Guid Id { get; set; }
        [DataMember]
        public string Name { get; set; }
        [IgnoreDataMember]
        public string Address { get; set; }
        [IgnoreDataMember]
        public int PinCode { get; set; }
        [DataMember]
        public string Phone { get; set; }
    }

    class Program
    {
        // list of properties of Person class marked with DataMemeber attribute
        private PropertyInfo[] _personProperties = typeof(Person)
            .GetProperties()
            .Where(x => x.GetCustomAttribute(typeof(DataMemberAttribute)) != null).ToArray();

        public string Serialize(string separator, IEnumerable<Person> objectlist)
        {
            StringBuilder tsvdata = new StringBuilder();

            string header = string.Join(separator, _personProperties.Select(x => x.Name).ToArray());
            tsvdata.AppendLine(header);
            foreach (var o in objectlist)
            {
                List<string> lineValues = new List<string>();
                // enumerate through the properties
                foreach (var pi in _personProperties)
                {
                    lineValues.Add(pi.GetValue(o).ToString());
                }
                tsvdata.AppendLine(string.Join(separator, lineValues.ToArray()));
            }

            return tsvdata.ToString();
        }

        static void Main(string[] args)
        {
            Program p = new Program();
            List<Person> persons = new List<Person> {
                new Person { Id = Guid.NewGuid(), Address = "a1", Name = "name1", Phone = "tel1", PinCode = 1111 },
                new Person { Id = Guid.NewGuid(), Address = "a2", Name = "name2", Phone = "tel2", PinCode = 2222 },
            };
            string serializedString = p.Serialize(", ", persons);
            Console.WriteLine($"result: {serializedString}");
        }
    }
}

This code enumarates all the properies of the POCO class, checks if they have a specific attribute, leaves out those, and outputs the rest.

You can define your own attribute as you wish, here I have just used IgnoreDataMember from System.Runtime.Serialization (used for similar purpose).

Note that checking for existing properties and attributes is using reflection, which should be used with some care. Performance may be an issue, if the code runs with large data sets many times, it can be slow -- that's why I put the personProperties out from the loop to a private member (computed once).

Comments