Suresh Sankar
Suresh Sankar

Reputation: 47

How to Split OData multi-level expand query string in C#?

I have a URL: Expand=User($select=Firstname,Lastname),Organisation,Contract($Expand=MyOrganisation($select=Name,Status),Organisation),List

I need to split this string in the below format:

How to achieve this functionality in C#?

Upvotes: 0

Views: 1147

Answers (2)

Amal K
Amal K

Reputation: 4854

You not only need to split the string but also keep track of the parentheses while splitting. This is not possible with just plain old regex. See this post. However, the splitting can be achieved with some advanced RegEx; .NET fortunately supports balancing groups using which you can keep track of the parentheses. This answer was quite helpful in coming up with a solution. For readability, I have split the regex into multiple lines and used RegexOptions.IgnorePatternWhitespace:

string url = "User($select=Firstname,Lastname),Organisation,Contract($Expand=MyOrganisation($select=Name,Status),Organisation),List";
Regex rgx = new Regex(
               @"(.+?)
                (
                    (
                        \(
                            (?:
                                [^()]
                                |
                                (?'open'\()
                                |
                                (?'close-open'\))
                            )+
                            (?(open)(?!))
                        \)
                    )
                    ,
                    |
                    ,
                    |
                    \b$
                )", 
               RegexOptions.IgnorePatternWhitespace);

foreach(var match in rgx.Matches(url))
{
    Console.WriteLine($"{match.Groups[1]} {match.Groups[3]}"); 
}

The field will be available as match.Groups[1] and the parameters, if any will be available as match.Groups[3](this will be an empty string if there are no parameters). You can access match.Groups[0] to get the entire group.

Regex Breakdown

Regex Description
(.+?) Non-greedily match one or more characters
\( and \) Match an actual ( and )
[^()] Match any character that is not a ( or )
(?'open'\() Create a named group with the name "open" and match a ( character
(?'close-open\)) Create a group "close" and assign the interval between "open" and "close" to "close" and delete group "open"
(?(open)(?!)) Assert if the "open" group is not deleted
(?:[^()]|(?'open'\()|(?'close-open'\)))+ Create a non-capturing group and match one or more characters that match one of the expressions between |

Upvotes: 2

Boris Sokolov
Boris Sokolov

Reputation: 1793

More likely you have to use an ODataLib with in-built URI Parser

Uri requestUri = new Uri("Products?$select=ID&$expand=ProductDetail" +
                         "&$filter=Categories/any(d:d/ID%20gt%201)&$orderby=ID%20desc" +
                         "&$top=1&$count=true&$search=tom",
                         UriKind.Relative);
ODataUriParser parser = new ODataUriParser(model, serviceRoot, requestUri);
SelectExpandClause expand = parser.ParseSelectAndExpand(); // parse $select, $expand
FilterClause filter = parser.ParseFilter();                // parse $filter
OrderByClause orderby = parser.ParseOrderBy();             // parse $orderby
SearchClause search = parser.ParseSearch();                // parse $search
long? top = parser.ParseTop();                             // parse $top
long? skip = parser.ParseSkip();                           // parse $skip
bool? count = parser.ParseCount();                         // parse $count

Adding the RegExp option (the fixed version of what Amal has provided below)

    string url = "User($select=Firstname,Lastname),Organisation,Contract($Expand=MyOrganisation($select=Name,Status),Organisation),List";

    Regex rgx = new Regex(@"(.+?)(?:(\(.*?\)),|,)");

    foreach (var match in rgx.Matches($"{url},"))
    {
        Console.WriteLine(match.ToString()[..^1]);
    }

Upvotes: 2

Related Questions