Search code examples
c#.netstring-formattingstring-interpolation

Format string with meaningful markers


I used have a template string, read from DB and or text file:

var template="Your order {0} has dispatched from {1}, ETA {2:HH:MM dd.MM.yyyy}";

And then used string.Format(template, orderNo, warehouse, eta) to inject correct values.

Now, a business decision was made, to change those templates to use more meaningful markers:

var template="Your order {orderNo} has dispatched from {warehouse}, ETA {eta:HH:MM dd.MM.yyyy}";

And now I'm not sure what would be the best way to inject values, taking into consideration that, for example, eta field has formatting that is "hardcoded" into template (so that we could use different DateTime format depending on language)

One idea was to load the template into StringBuilder, replace named variables into set of {0}, {1}.. etc, And then use old string.Format method, but it's not really scalable and pain in the back to write all the boilerplate.

var formatTemplate = new StringBuilder(template)
    .Replace("{orderNo}", "{0}")
    .Replace("{warehouse}", "{1}")
    .Replace("{eta","{2")
    .ToString();
return string.Format(template, orderNo, warehouse, eta)

Is there a better way to do it? Maybe a way to read the template as interpolated string? The string is read from an outside source so I cannot simply use the $"{marker}" mechanic.


Solution

  • Please check out SmartFormat which in effect will do what you want to.

    (You sayed, that you do not use replacing the human readable names with the indexes)

    Example from their wiki:

    String.Format references all args by index:

    String.Format("{0} {1}", person.FirstName, person.LastName)
    

    Smart.Format takes this a step further, and lets you use named placeholders instead:

    Smart.Format("{FirstName} {LastName}", person)
    

    In fact, Smart.Format supports several kinds of expressions:

    Smart.Format("{FirstName.ToUpper} {LastName.ToLower}", person)
    

    Working example requires the variables to be packed into an anonymous type:

    var formatted=Smart.Format(template, new { orderNo, warehouseName, eta })
    

    that returns correct, desired, result, with markers correctly replaced. Without the anonymous type it didn't seem to work.