11

We have a template URL like:

http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}

and I have values for these constant tokens. How can replace all these tokens in C#?

zzlalani
  • 22,960
  • 16
  • 44
  • 73
eLs
  • 213
  • 2
  • 3
  • 8
  • 1
    Look up `string.Replace` on msdn. – Blorgbeard Nov 29 '13 at 04:55
  • is this url you want to store somewhere and send it or is it coming in your request??? – Rudresha Parameshappa Nov 29 '13 at 04:57
  • possible duplicate of [Named string formatting in C#](http://stackoverflow.com/questions/159017/named-string-formatting-in-c-sharp) – Oliver Nov 29 '13 at 08:53
  • Side note on example URL: when using http instead of https for the url then the url is sent unencrypted. Thus if someone is sniffing your traffic, he can see the username and password in the url (and it may be logged in server logs on the way). – CJBS Sep 29 '17 at 18:33

6 Answers6

13

A simple approach is to use a foreach and a Dictionary with a String.Replace:

var values = new Dictionary<string, string> {
    { "{networkid}", "WHEEE!!" }
    // etc.
};
var url = "http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";

foreach(var key in values.Keys){
    url = url.Replace(key,values[key]);
}
CJBS
  • 15,147
  • 6
  • 86
  • 135
Grundy
  • 13,356
  • 3
  • 35
  • 55
  • 2
    Nice way to borrow my replacement value ;-) Well, it is pretty simple to understand, so +1. – user2864740 Nov 29 '13 at 05:45
  • This algorithm runs slowly in time O(t * r) and will give incorrect results if one of the replacement values contains a token for later replacement. [Details](https://stackoverflow.com/a/71826824/145173) – Edward Brey Apr 11 '22 at 11:15
  • @EdwardBrey, this is feature, not a bug :-D – Grundy Apr 11 '22 at 13:49
  • @Grundy That's funny because it's kind of true. Applying replacements recursively can be useful. To be clear, that would take a more sophisticated algorithm, one that rescanned for replacements as a second complete pass (and then again with subsequent passes until no replacements were left), to avoid missing recursive replacements occurring late in the template. It would also need to check for infinite recursion. – Edward Brey Apr 11 '22 at 15:51
  • @EdwardBrey, for now, here not a recursion, just one way replacing in loop by keys. Any way this just as simple as possibly solution :) Straight replacement without any regexp – Grundy Apr 11 '22 at 16:02
  • It depends on the replacement values. For example, suppose `optinfo` is `excludeFromReceipt:{publisher},{userId}`, `publisher` is `Acme`, and `userId` is `14`. With successive full-string replacements, you'd get ...`&optinfo=excludeFromReceipt:Acme,14&publisher=Acme&msisdn=14` instead of ...`&optinfo=excludeFromReceipt:{publisher},{userId}&publisher=Acme&msisdn=14`. – Edward Brey Apr 11 '22 at 19:01
  • Also worth noting is while, yes, using string.Replace is simpler in that it doesn't use Regex, it has its own complexity that a Regex-based approach avoids. Specifically, using string.Replace requires you manage your own iteration and variable mutation. Using Regex shields you from both making it simpler to prove correctness for all input cases. – Edward Brey Apr 11 '22 at 19:05
9

There is no standard way to "replace with dictionary values" in .NET. While there are a number of template engines, it's not very hard to write a small solution for such an operation. Here is an example which runs in LINQPad and utilizes a Regular Expression with a Match Evaluator.

As the result is a URL, it is the callers responsibility to make sure all the supplied values are correctly encoded. I recommend using Uri.EscapeDataString as appropriate .. but make sure to not double-encode, if it is processed elsewhere.

Additionally, the rules of what to do when no replacement is found should be tailored to need. If not-found replacements should be eliminated entirely along with the query string key, the following can expand the regular expression to @"\w+=({\w+})" to also capture the parameter key in this specific template situation.

string ReplaceUsingDictionary (string src, IDictionary<string, object> replacements) {
    return Regex.Replace(src, @"{(\w+)}", (m) => {
        object replacement;
        var key = m.Groups[1].Value;
        if (replacements.TryGetValue(key, out replacement)) {
            return Convert.ToString(replacement);
        } else {
            return m.Groups[0].Value;
        }
    });
}

void Main()
{
    var replacements = new Dictionary<string, object> {
        { "networkid", "WHEEE!!" }
        // etc.
    };
    var src = "http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
    var res = ReplaceUsingDictionary(src, replacements);

    // -> "http://api.example.com/sale?..&networkid=WHEEE!!&..&pageid={pageid}&..
    res.Dump();
}

More advanced techniques, like reflection and transforms, are possible - but those should be left for the real template engines.

Community
  • 1
  • 1
user2864740
  • 60,010
  • 15
  • 145
  • 220
  • A simplified Regex-based algorithm that takes replacements as strings is [here](https://stackoverflow.com/a/71826824/145173). – Edward Brey Apr 11 '22 at 11:20
  • @EdwardBrey Indeed. Looking at the code above 5 years later, it appears I was still stuck on C#5 at the time.. many language improvements since. – user2864740 Jun 05 '22 at 19:07
5

I am guessing you are trying to replace parameters in url with your values. This can be done using C# HttpUtility.ParseQueryString

Get the CurrentURL from

   var _myUrl = System.Web.HttpUtility.ParseQueryString(Request.RawUrl);

Read Parameter from your Query string

   string value1 = _myUrl ["networkid"];

Write a value into the QueryString object.

  _myUrl ["networkid"] = "Your Value";

and then finally turn it back into URL

  var _yourURIBuilder= new UriBuilder(_myUrl );
 _myUrl = _yourURIBuilder.ToString();
Rohit
  • 10,056
  • 7
  • 50
  • 82
3

You can use this alos using LinQ

Dictionary<string, string> myVal = new Dictionary<string, string>();

myVal.Add("networkid", "1");
myVal.Add("pageid", "2");
myVal.Add("master", "3");
myVal.Add("optinfo", "4");
myVal.Add("publisher", "5");
myVal.Add("userId", "6");

string url = @"http://api.example.com/sale?auth_user=xxxxx&auth_pass=xxxxx&networkid={networkid}&category=b2c&country=IT&pageid={pageid}&programid=133&saleid=1&m={master}&optinfo={optinfo}&publisher={publisher}&msisdn={userId}";
myVal.Select(a => url = url.Replace(string.Concat("{", a.Key, "}"), a.Value)).ToList();

this line can do your required functionlity

myVal.Select(a => url = url.Replace(string.Concat("{", a.Key, "}"), a.Value)).ToList();

Neeraj Kumar Gupta
  • 2,157
  • 7
  • 30
  • 58
2

There is a Nuget called StringTokenFormatter that does this well https://www.nuget.org/packages/StringTokenFormatter/

Dani Avni
  • 423
  • 5
  • 14
1

Regex.Replace makes a single pass over a template string, offering you an opportunity to replace matched expressions. Use it by creating an regular expression that matches any token. Then look up replacement values for the tokens in a dictionary.

static string ReplaceTokens(string template, Dictionary<string, string> replacements) =>
    Regex.Replace(template, @"{(\w+)}",
        match => replacements.TryGetValue(match.Groups[1].Value, out string replacement) ? replacement : match.Value);

The algorithm completes in time linear with the size of the template string and the replacement strings, so O(t + r). Beware of algorithms that make multiple passes. They run slowly in time O(t * r) and will give incorrect results if one of the replacement values contains a token for later replacement. This unit test shows the pitfall:

public void TestReplaceTokens() {
    var replacements = new Dictionary<string, string> {
        ["Movement"] = "drive {DontReplace}",
        ["DontReplace"] = "Should not appear"
    };
    string withReplacements = ReplaceTokens("I always {Movement} in {Direction}.", replacements);
    Assert.AreEqual("I always drive {DontReplace} in {Direction}.", withReplacements);
}
Edward Brey
  • 40,302
  • 20
  • 199
  • 253