5

I'm wondering, is there any standard way to parse such path-like string:

Server[@Name='MyServerName']/Database[@Name='MyDatabaseName']/Table[@Name='MyTableName' and @Schema='MySchemaName']

The result must be:

  • ItemName (Server)
    • PropertyName (Name), PropertyValue (MyServerName)
  • ItemName (Database)
    • PropertyName (Name), PropertyValue (MyDatabaseName)
  • ItemName (Table)
    • PropertyName (Name), PropertyValue (MyTableName)
    • PropertyName (Schema), PropertyValue (MySchemaName)

Most obvious here is to make a regular expression (and, of course, String.Split), but may be there's a better, standard way?

For the information: the string comes from SMO's Urn.Value.

UPDATE.

The answer is found, see below.

Dennis
  • 37,026
  • 10
  • 82
  • 150
  • 9
    A standard way to parse a custom string? :) – default Aug 21 '12 at 06:50
  • 4
    That looks like an [XPath](http://www.w3.org/TR/xpath/). You might want to look into the [respective classes](http://msdn.microsoft.com/en-us/library/system.xml.xpath.xpathdocument.aspx) of the .NET framework. Other than that you would have to provide your custom code to do it, or look into ANTLR which has an [xpath grammar](http://www.antlr.org/grammar/list) (never looked at it, YMMV). – Christian.K Aug 21 '12 at 06:53
  • If you have a way to parse this -> `PropertyName (Schema), PropertyValue (MySchemaName)` Then all you need to do is aggregate/combine it for the rest of your structure. – gideon Aug 21 '12 at 06:54
  • 1
    @Default: I'm understanding your sarcasm, but may be this string has any type of well-known syntax, which I don't know? I don't like to invent yet another wheel. – Dennis Aug 21 '12 at 06:57
  • @Christian.K: yes, I'm thinking about XPath too... But I can't understand, how to apply XPath here. – Dennis Aug 21 '12 at 07:00
  • 1
    The result is `Dictionary<>` or XML? – Sergey Vyacheslavovich Brunov Aug 21 '12 at 07:02
  • Are you looking for a standard data structure or only parsing, because you could store this as a `Dictionary>`. Except ofc if you want to store the `and` somewhere. – gideon Aug 21 '12 at 07:05
  • @Serge: the both will suit me, but XML will be preferred result. – Dennis Aug 21 '12 at 07:09
  • @Dennis You cannot do much with the XPath classes (of the .NET framework) per se. You need a "stand alone" XPath parser (see my first comment for a link). Note that could be more work than simply writing hand-crafted/specific code. In the end, even with a stock XPath parser, you'd still need to interpret the results so that they make sense to you (the parser obviously doesn't know about "Database" for example). – Christian.K Aug 21 '12 at 07:21
  • Possible duplicate: http://stackoverflow.com/questions/514879/parse-xpath-expressions – Sergey Vyacheslavovich Brunov Aug 21 '12 at 07:23

4 Answers4

5

Well, I did it. :)

The answer is very simple. There's Urn.XPathExpression property. But it's return value type is Microsoft.SqlServer.Management.Sdk.Sfc.XPathExpression, not a System.Xml.XPath.XPathExpression (this confused me).

And XPathExpression from SMO has all necessary features to parse itself:

        var urn = new Urn("Server[@Name='MyServerName']/Database[@Name='MyDatabaseName']/Table[@Name='MyTableName' and @Schema='MySchemaName']");

        for (var i = 0; i < urn.XPathExpression.Length; i++)
        {
            Console.WriteLine("ItemName ({0})", urn.XPathExpression[i].Name);

            foreach (DictionaryEntry item in urn.XPathExpression[i].FixedProperties)
            {
                Console.WriteLine("\tPropertyName ({0}), PropertyValue ({1})", item.Key, item.Value);
            }
        }

produces this output:

ItemName (Server)
        PropertyName (Name), PropertyValue ('MyServerName')
ItemName (Database)
        PropertyName (Name), PropertyValue ('MyDatabaseName')
ItemName (Table)
        PropertyName (Name), PropertyValue ('MyTableName')
        PropertyName (Schema), PropertyValue ('MySchemaName')

Christian.K, thanks for the hint about XPath!

Dennis
  • 37,026
  • 10
  • 82
  • 150
1

Maybe a little late, but i was looking for the same answer, then stumbled apon this on my own. Urn has a couple of Get functions.

 var urn = new Urn("Server[@Name='MyServerName']/Database[@Name='MyDatabaseName']/Table[@Name='MyTableName' and @Schema='MySchemaName']");
    Console.WriteLine(urn.GetNameForType("Table"));

Would result in "MyTableName"

Yogurt The Wise
  • 4,379
  • 4
  • 34
  • 42
0

Don't have a code app to test, but something like

var resultArr=str.Split(new char[]{'/', '[',']','@', '='}); should create an array of artifacts you want.

The only thing that remains is to identify correct array index for each of them.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • Tigran, thanks for the answer. I have to be more clear in my question: I want to avoid regex'es and `String.Split`. – Dennis Aug 21 '12 at 07:06
  • @Denis: if there is nothing yet done (it's not a standart string) you need to write a small *parser*. But it sound like cumbersome solution for such task, so if you dont't have **really** serious reasons to not using `Split(..)` or any other built-in function, would suggest to use them. – Tigran Aug 21 '12 at 07:16
  • @alexn: If you will need to parse 'http://host.domain.com/service?param1=value&param=2' value, will you use `UriBuilder` class? Or you will parse it with `String.Split`? This string **looks like** path, or xpath expression... Of course, if it is not standard, I will use regex'es. – Dennis Aug 21 '12 at 07:17
  • @Dennis I would use `Uri.Parse`. `String.Split` / `Regex` is a easy and lightweight solution to this problem. Using ANTLR and a XPath grammar is overkill if you only need to parse that simple string. Example: https://gist.github.com/3413106. – alexn Aug 21 '12 at 07:29
0

I believe, the only standard way would be using regular expression with pattern matching.

Following code snippet will help -

    string input = @"Server[@Name='MyServerName']/Database[@Name='MyDatabaseName']/Table[@Name='MyTableName' and @Schema='MySchemaName']";
    string elementPattern = @"(?<ItemName>\w+)\[@(?<PropertyName>\w+)='(?<PropertyValue>\w+)'( and @(?<PropertyName>\w+)='(?<PropertyValue>\w+)')*\]";

    Regex elementRegex = new Regex(elementPattern, RegexOptions.Compiled);

    string[] elements = input.Split('/');

    foreach (string element in elements)
    {
        Match m = elementRegex.Match(element);

        if (m.Success)
        {
            Console.WriteLine("ItemName ({0})", m.Groups["ItemName"]);

            foreach (Capture capture in m.Groups["PropertyName"].Captures)
            {
                Console.WriteLine("PropertyName ({0})", capture.Value);
            }

            foreach (Capture capture in m.Groups["PropertyValue"].Captures)
            {
                Console.WriteLine("PropertyValue ({0})", capture.Value);
            }
        }
    }

    Console.ReadKey();

Output: -

ItemName (Server)
PropertyName (Name)
PropertyValue (MyServerName)
ItemName (Database)
PropertyName (Name)
PropertyValue (MyDatabaseName)
ItemName (Table)
PropertyName (Name)
PropertyName (Schema)
PropertyValue (MyTableName)
PropertyValue (MySchemaName)

Note: Please make changes as per your requirement.

Parag Meshram
  • 8,281
  • 10
  • 52
  • 88
  • Thanks, but "Most obvious here is to make a regular expression" - citation from my question. – Dennis Aug 21 '12 at 07:53