0

I want to create a parser that convert string tokens to typed objects based on a key.

My first stab, borrowing ideas from Dictionary<T,Delegate> with Delegates of different types: Cleaner, non string method names?

delegate T Parser<T>(string item);

public class TableParser    
{
    static IDictionary<string, Pair<PropertyInfo, Delegate>> _PARSERS;
    static Type DOMAIN_TYPE;

    static TableParser()
    {
        DOMAIN_TYPE= typeof(Domain);

        Dictionary<string, Pair<PropertyInfo, Delegate>> parsers = new
            Dictionary<string, Pair<PropertyInfo, Delegate>>()
        {
            { "PropertyOne", new Pair<PropertyInfo,Delegate>(
                DOMAIN_TYPE.GetProperty("PropertyOne"), 
                (Parser<double>) double.Parse ) },
        };
        _PARSERS = parsers;
    }

    public List<Domain> Parse(string filename)
    {
        List<Domain> domains = new List<Domain>();

        List<List<string>> data = 
            CVSParser.Instance.Parse(filename);
        List<string> headers = data[0];

        for (int i = 1; i < data.Count; i++)
        {
            List<string> row = data[i];
        }            

        return domains;
    }

    private Dictionary<int, Pair<PropertyInfo, Delegate>> FindParsers(List<string> headers)
    {
        Dictionary<int, Pair<PropertyInfo, Delegate>> parsers =
            new Dictionary<int, Pair<PropertyInfo, Delegate>>();

        int i = 0;
        headers.ForEach(h =>
        {
            if (_PARSERS.ContainsKey(h))
            {
                parsers[i] = _PARSERS[h];
            }
            ++i;
        });

        return parsers;
    }

    private Domain Create(List<string> data,
        Dictionary<int, Pair<PropertyInfo, Delegate>> parsers)
    {
        Domain domain = new Domain();

        foreach (KeyValuePair<int, Pair<PropertyInfo, Delegate>> parser in parsers)
        {
            string datum = data[parser.Key];
            parser.Value.First.SetValue(domain,
                /* got stuck here */ parser.Value.Second,
                null);

        }

        return domain;
    }
}

I got stuck at re-discovering the type of the parser when I need to use it. I need to cast it back to Parser<double>, Parser<int>, etc depending on PropertyInfo.

A canned CSV parser doesn't work in this case because Domain's properties come from multiple files.

Community
  • 1
  • 1
Candy Chiu
  • 6,579
  • 9
  • 48
  • 69

3 Answers3

0

If its not absolutely necessary to create your own parser, use Irony. Its in C# and easy to use. Why re-invent?!

Nayan
  • 3,092
  • 25
  • 34
  • it is nice, but too much for our team. we only need to convert tokens to objects. – Candy Chiu Nov 01 '11 at 21:38
  • Alright! But keep in mind, the more you code, more bugs you create! :) Plus, implementing grammar in Irony will take lesser than 30 mins for whole language. – Nayan Nov 02 '11 at 12:55
0

If you want to new up object based on some key, please have a look at this question, it seems quite similar. In your case it sounds like you wouldn't even have to use reflection because the types of objects you want to create are already known.

Can they all share an Interface? in that case you can have an abstract MyInterface CreateOne() method that news up an instance that you can implement on each class. Else the approach from the question would probably work best.

Regards GJ

Community
  • 1
  • 1
gjvdkamp
  • 9,929
  • 3
  • 38
  • 46
  • I need to convert string to double using double.Parse. – Candy Chiu Nov 01 '11 at 22:26
  • So... What exactly is keeping you from doing that? I didn't get that from the question. Do you have some sample of the file you're parsing and where exactly you get stuck? – gjvdkamp Nov 01 '11 at 22:44
0

Although I may not have the full picture, it looks like you're making things more complicated than they need to be.

A simple switch on the PropertyType and direct invocations of the appropriate parse methods would make the code simpler to understand and maintain. If the cost of reflection worries you, use a library to generate delegates that refer directly to the property.

That said, to make your current solution work, the easiest is probably to use dynamic invocation of the delegates:

parser.Value.First.SetValue(analytic, 
    parser.Value.Second.DynamicInvoke(datum),
    null);

I also cannot resist mentioning the extension methods provided by the library referenced above, which allows you to create object instances from a set of values (the data types do not need to match, types are automatically converted/coerced as needed), like this:

var properties = new [] { "One", "Two" };
var inputValues = new object[] { 1.0d, "foobar" };
var domain = typeof(Domain).TryCreateInstance( properties, inputValues );
Morten Mertner
  • 9,414
  • 4
  • 39
  • 56