2

I am working on a C# project which basically works on commands to perform certain operations. It uses CommandLineParser library to parse all these commands and respective argument.

As per the new business requirement, I need to parse an URL which contains verb and parameters.

Is there any built-in/easy way to parse URL in CommandLineParser library, that will avoid writing custom logic which I explained in below approach?


Here is my approach to solve this challenge:

Covert URL to command, then using CommandLine parser assign values to option properties.

e.g.

URL:

https://localhost:9000/Open?appId=123&appName=paint&uId=511a3434-37e0-4600-ab09-65728ac4d8fe

Implementation:

  • Custom logic to covert url to command

    open -appId 123 -name paint -uid 511a3434-37e0-4600-ab09-65728ac4d8fe
    
  • Then pass it to parser.ParseArguments

  • Here is the class structure

    [Verb("open", HelpText = "Open an application")]
    public class OpenOptions
    {
        [Option("id", "appId", Required = false, HelpText = "Application Id")]
        public int ApplicationId{ get; set; }
        [Option("name", "appName", Required = false, HelpText = "Application Name")]
        public string ApplicationName{ get; set; }
        [Option("uId", "userId", Required = true, HelpText = "User Id")]
        public Guid UserId{ get; set; }
    }
    
Matt
  • 25,467
  • 18
  • 120
  • 187
Prasad Telkikar
  • 15,207
  • 5
  • 21
  • 44
  • 1
    CommandLineParser can't easily parse the syntax of a URL. For that purpose you should take a look at the [`Uri` class](https://learn.microsoft.com/en-us/dotnet/api/system.uri) that is mentioned [here](https://stackoverflow.com/a/29434651/1838048). – Oliver Feb 14 '23 at 07:12
  • The question is unclear. The job of CommandLineParser is to parse the command line, not the arguments themselves. You may (or may not) be able to create a custom argument parser to extract data from a specific argument. You didn't post any command line sample though, explain what you want to parse or what you want to do with it. Parsing a URL is the job of eg the Uri class or one of the many Http Utility methods in various .NET libraries – Panagiotis Kanavos Feb 14 '23 at 07:43
  • @PanagiotisKanavos, my questoin is can we parse url using command line parser? I guess I can't do it. I have to use Uri class as you said in the comment. Thanks – Prasad Telkikar Feb 14 '23 at 07:47
  • That's a bad question. Why not ask if you can use `decimal.Parse`? It's just as unrelated to URLs. You can parse URL arguments though. So what *is* the question? Why CommandLineParser specifically? – Panagiotis Kanavos Feb 14 '23 at 07:50
  • @PanagiotisKanavos My original question is `Is there any built-in/easy way to parse URL in CommandLineParser library?` If it is not there then that is ok. I have already written a code which converts url to command. Below answers also explain the same. My intension behind asking this question was to not break existing infrastructure of parsing command and just do some tweaks to support URL parsing as well. I don't think I will ever ask how to parse url using `decimal.Parse` – Prasad Telkikar Feb 14 '23 at 07:54

3 Answers3

3

There is no out-of-the-box solution. Normally HttpUtility class (ref Get URL parameters from a string in .NET) can do the trick.

Here is the first approach:

var url = "https://localhost:9000/Open?appId=123&appName=paint&uId=511a3434-37e0-4600-ab09-65728ac4d8fe";
Uri uri = new Uri(url);
var @params = HttpUtility.ParseQueryString(uri.Query);
string? appId = @params.Get("appId");
Console.WriteLine(appId);

Will return 123, and so on with your other parameters.

The returned value will be a string type. You can eventually use Guid.TryParse to parse your Guid object and int.TryParse for an int value.

Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
  • Does that means, I need to write custom logic to get covert url to command, right? There is no straight forward way to use command line parser to parse the URL? – Prasad Telkikar Feb 14 '23 at 07:42
  • That is correct, a little method that takes URL and key and return the value – Maytham Fahmi Feb 14 '23 at 07:44
  • @PrasadTelkikar what's your actual question in the first place? A URL isn't a command line. There are a lot of classes that can parse URLs. Are you try to pass a URL as a command-line argument and have it parsed automatically? Why not use a `Uri` argument instead, and extract the parts afterwards? – Panagiotis Kanavos Feb 14 '23 at 07:45
  • @PanagiotisKanavos, thanks for the suggestion. Yes I will use `Uri` class to parse `Url` – Prasad Telkikar Feb 14 '23 at 07:57
  • If the question is how to parse query strings, there are several duplicate questions. Using `HttpUtility.ParseQueryString` is one option. It's still unclear what any of this has to do with command lines. It would make sense to parse a URL if you wanted to use a protocol handler for your application. – Panagiotis Kanavos Feb 14 '23 at 07:58
  • Can't you just pass `url` instead of `uri.Query` to ParseQueryString? I think that you don't need `Uri uri = new Uri(url);` at all. And when I tried it, `.Get("appId")` returned null. – Matt Feb 14 '23 at 13:07
  • @MaythamFahmi - Because it is not working as described. Please correct it and I will upvote it again. The NameValueCollection contains "https://localhost:9000/Open/appId", which is wrong. The other two parameters are correctly stored. That's why it is returning null at the moment for "appId". – Matt Feb 14 '23 at 13:11
  • Ah, ok - .Query returns "?appId=123&appName=paint&uId=511a3434-37e0-4600" so you can't pass the entire string url to HttpUtility.ParseQueryString. You are right, in this case it works. Still, you should store the result of ParseQueryString in a variable, so you don't have to parse over and over for each parameter (just an optimization suggestion). – Matt Feb 14 '23 at 13:21
  • I can and I will, but SO locks a vote unless you edit the question. I suggest you optimize it like `var args = HttpUtility.ParseQueryString(uri.Query); var appId=args.Get("appId");` then I'll pick it up and upvote ;-) – Matt Feb 14 '23 at 13:27
1

CommandLineParser is not made for parsing Uri's. But using the Uri class and Linq, this is quite easy:

using System;
using System.Collections.Generic;
using System.Linq;

public class Program
{
    public static void Main()
    {
        var (verb, query) = ParseUri("https://localhost:9000/Open/appId=123&appName=paint&uId=511a3434-37e0-4600-ab09-65728ac4d8fe");

        var appId = query["appId"];
        var appName = query["appName"];
        var uId = query["uId"];

        Console.WriteLine($"verb='{verb}'");
        Console.WriteLine($"appId='{appId}', appName='{appName}', uId='{uId}'");
    }

    static (string verb, Dictionary<string, string> args) ParseUri(string strUri)
    {
        var uri = new Uri(strUri);

        // get the verb
        var verb = uri.Segments[1].TrimEnd('/');

        // get the query part as dictionary
        var args = uri.Segments[2].Split('&')
            .Select(s => s.Split('=')).ToDictionary(d => d[0], d => d[1]);

        return (verb, args);    
    }

}

First parsing happens by instantiating an Uri allowing to use the Segments property. Then split the query by using & and finally create a dictionary by using = to split the key/value pair.

Because query is a Dictionary, you can simply access individual query parameters like:

var appId = query["appId"];
var appName = query["appName"];
var uId = query["uId"];

Console.WriteLine($"verb='{verb}'");
Console.WriteLine($"appId='{appId}', appName='{appName}', uId='{uId}'");

which prints:

verb='Open'
appId='123', appName='paint', uId='511a3434-37e0-4600-ab09-65728ac4d8fe'


Note: In LinqPad, you can use query.Dump(verb); to visualize the result:

enter image description here

Matt
  • 25,467
  • 18
  • 120
  • 187
0
var link = "https://localhost:9000/Open?appId=123&appName=paint&uId=511a3434-37e0-4600-ab09-65728ac4d8fe";

var uri = new Uri(link);
var command = uri.LocalPath.TrimStart('/').ToLower();
var query = HttpUtility.ParseQueryString(uri.Query);

var sb = new StringBuilder($"{command} ");
foreach(string key in query)
{
    sb.Append($"-{key} {query.Get(key)} ");
}

var commandWithArgs = sb.ToString();
Arvis
  • 8,273
  • 5
  • 33
  • 46