07 March 2011

How to retrieve Xml node xPath with c#


 public static string GetXPath(this XmlNode node)
        {
            if (node.NodeType == XmlNodeType.Attribute)
            {
                // attributes have an OwnerElement, not a ParentNode; also they have
                // to be matched by name, not found by position
                return String.Format(
                    "{0}/@{1}",
                    GetXPath(((XmlAttribute)node).OwnerElement),
                    node.Name
                    );
            }

            if (node.ParentNode == null)
            {
                // the only node with no parent is the root node, which has no path
                return "";
            }

            //get the index of the node
            int iIndex = 1;
            XmlNode xnIndex = node;

            while (xnIndex.PreviousSibling != null)
            {
                iIndex++;
                xnIndex = xnIndex.PreviousSibling;
            }

            // the path to a node is the path to its parent, plus "/node()[n]", where
            // n is its position among its siblings.
            return String.Format(
                "{0}/{1}[{2}]",
                GetXPath(node.ParentNode),
                node.Name,
                iIndex
                );
        }
Place holder in c# .net - Parser

look on it,

Lets define a place holder as follow :  identified string with some meaning.

Identification :

          {placeHolder:property}

e.g.

          Data Source={connection_string:server}

place holder represented by concrete classes, produce by factory, parsed by engine.

lets look on the engine,




public string Parse(string command)
        {
            char[] commandChArray = command.ToCharArray(0, command.Length);


            int index = 0;


            StringBuilder outputMainCommand = new StringBuilder();


            // each place holder command may consist of place holder (aka command name) and property    
            // (aka command value).
            StringBuilder executeCommandName = new StringBuilder();
            StringBuilder executeCommandData = new StringBuilder();


         
            bool isCommandContext = false;
            bool isCommandData = false;
            bool isCommandName = false;


            int closeBracket = 0;
            while (index < commandChArray.Count())
            {
                switch (commandChArray[index])
                {
                    // encounter with {
                    case START_COMMAND_IDENTIFIER:
                        
                        closeBracket++;


                        if (!isCommandContext)
                        {
                            closeBracket = 1;


                            executeCommandName.Clear();
                            executeCommandData.Clear();


                            isCommandContext = true;
                            isCommandName = true;
                            isCommandData = false;
                        }
                        else
                        {
                            if (isCommandName)
                            {
                                executeCommandName.Append(commandChArray[index]);
                            }
                            else if (isCommandData)
                            {
                                executeCommandData.Append(commandChArray[index]);
                            }
                        }
                        break;
                   
                   // encounter with }
                   case END_COMMAND_IDENTIFIER:
                        
                       closeBracket--;


                       if (isCommandContext && closeBracket == 0)
                        {                            
                            try
                            {                                
                                string commandName = string.Empty;


                                if (executeCommandName != null)
                                {
                                    commandName = executeCommandName.ToString();
                                }


                                string commandData = string.Empty;
                                if (executeCommandData != null)
                                {
                                    commandData = executeCommandData.ToString();
                                }


                                if (!string.IsNullOrEmpty(commandName))
                                {
                                    string output = GetPlaceHolderHandler(commandName).FromPlaceHolder(commandData);


                                    outputMainCommand.Append(output);
                                }
                                else
                                    // common
                                {
                                    outputMainCommand.Append(commandChArray[index]);
                                }
                            }
                            finally
                            {
                                executeCommandName.Clear();
                                executeCommandData.Clear();


                                isCommandContext = false;
                                isCommandName = true;
                                isCommandData = false;    
                            }
                        }
                        if (isCommandContext)
                        {
                            if( isCommandName )
                            {
                                executeCommandName.Append(commandChArray[index]);
                            }
                            else if( isCommandData)
                            {
                                executeCommandData.Append(commandChArray[index]);
                            }
                        }
                                             
                        break;


                    case COMMAND_SEPERATOR_IDENTIFIER:


                        if (isCommandContext)
                        {
                            isCommandName = false;


                            if (isCommandData)
                            {
                                executeCommandData.Append(commandChArray[index]);
                            }


                            isCommandData = true;


                        }
                        else
                            // common
                        {
                            outputMainCommand.Append(commandChArray[index]);
                        }


                        break;


                    default:
                        if (isCommandContext)
                        {
                            if( isCommandName )
                            {
                                executeCommandName.Append(commandChArray[index]);
                            }
                            else if( isCommandData)
                            {
                                executeCommandData.Append(commandChArray[index]);
                            }
                        }
                        else
                        {
                            outputMainCommand.Append(commandChArray[index]);
                        }                       
                        break;
                }


                index++;
            }


            return outputMainCommand.ToString();
        }


Parse(string command) input example :  Data Source={connection_string:server}.

How to improve it ?
Add parallel to the parser, split the data to be parsed into command chunks and streaming the command value to the required command place holder, think on command defined from many place holder, all should be parsed.
Hello,

Some days ago, i was requested to add rules to our project, the rule have some rules: must be configured (outside of code), provide the  rule part of the current xml implementation, readable and of course, we dont have such time to do it, we have zero time.

First i decide to have this ability part of our flow xml data.

Rule definition lang should bring the ability to read/change/maintain/update/ add new functionllity in future/ etc the rule.

So, Rule should be xml or something closer.

In order to have within a xml document (my old and pretty xml files) a rule definition language, i had some ideas how to do it,

1. CDATA
2. Regular Expression
3. Json

I decided on json.

Why ?

CData, complex to read and maintains.

Regular expression, hard to maintain/read, not everyone familiar with it (its a pity but this is the reality)

So, i decide  to walk on Json,

Using json.net, it was very simple, just having xml with a json string, readable, simple and maintainable,

Before code, the rule should calculate date 30 days ago and 15 days ahead (this may change in production).

Let's code :

this is the json.net impl class


    [JsonObject(MemberSerialization.OptOut)]
    public class DatePlaceHolderDataItem : BasePlaceHolderDataItem
    {
        [JsonProperty]
        public Format Format { get; set; }


        [JsonProperty(PropertyName = "CalcBy")]
        public DateCalculation DateCalculation
        {
            get;
            set;
        }
    }


    [JsonObject(MemberSerialization.OptOut)]
    public class DateCalculateBy
    {
        [JsonProperty]
        public int Value { get; set; }
    }


....

i have defined new class BasePlaceHolderDataItem as follow :


public class BasePlaceHolderDataItem 
    {
        protected virtual Formatting SerializeFormattingType
        {
            get { return Formatting.None; }
        }


        public virtual string Serialize()
        {
            try
            {
                return JsonConvert.SerializeObject(this, SerializeFormattingType);
            }
            catch(Exception  exception)
            {
                throw new PlaceHolderException("Failed to Serialize place holder object.", exception);
            }
        }


        public static T Deserialize(string jsonDataItem)
        {
            if (string.IsNullOrEmpty(jsonDataItem))
            {
                return default(T);
            }


            try
            {
                return JsonConvert.DeserializeObject(jsonDataItem);
            }
            catch (Exception exception)
            {
                throw new PlaceHolderException( string.Format("Failed to Deserialize place holder string : {0}.", jsonDataItem), exception);
            }
        }
    }


lets examine the impl,

the FromPlaceHolder  identify place holder are parse it using the Deserialize method, invoke the ParsePlaceHolderByDataItem,  parse the object itself, simple and readable.

        public string FromPlaceHolder(string placeHolderData)

        {
            // DatePlaceHolderDataItem
            if (placeHolderData.StartsWith("{"))
            {
                DatePlaceHolderDataItem datePlaceHolderDataItem = DatePlaceHolderDataItem.Deserialize(placeHolderData);
               
                if( datePlaceHolderDataItem != null )
                {
                    return ParsePlaceHolderByDataItem(datePlaceHolderDataItem);
                }                
            }


            return ParsePlaceHolderByFormat(placeHolderData);            
        }


        private string ParsePlaceHolderByDataItem(DatePlaceHolderDataItem datePlaceHolderDataItem)
        {
            DateTime dateTime = DateTime.Now;


            if( datePlaceHolderDataItem.DateCalculation != null )
            {
                if( datePlaceHolderDataItem.DateCalculation.Year != null )
                {
                    dateTime = dateTime.AddYears(datePlaceHolderDataItem.DateCalculation.Year.Value);
                }
            }


            if (datePlaceHolderDataItem.DateCalculation != null)
            {
                if (datePlaceHolderDataItem.DateCalculation.Month != null)
                {
                    dateTime = dateTime.AddMonths(datePlaceHolderDataItem.DateCalculation.Month.Value);
                }
            }


            if (datePlaceHolderDataItem.DateCalculation != null)
            {
                if (datePlaceHolderDataItem.DateCalculation.Day != null)
                {
                    dateTime = dateTime.AddDays(datePlaceHolderDataItem.DateCalculation.Day.Value);
                }
            }


            if (datePlaceHolderDataItem.DateCalculation != null)
            {
                if (datePlaceHolderDataItem.DateCalculation.Hour != null)
                {
                   dateTime = dateTime.AddHours(datePlaceHolderDataItem.DateCalculation.Hour.Value);
                }
            }


            if (datePlaceHolderDataItem.DateCalculation != null)
            {
                if (datePlaceHolderDataItem.DateCalculation.Minute != null)
                {
                    dateTime = dateTime.AddMinutes(datePlaceHolderDataItem.DateCalculation.Minute.Value);
                }
            }


            if (datePlaceHolderDataItem.DateCalculation != null)
            {
                if (datePlaceHolderDataItem.DateCalculation.Second != null)
                {
                    dateTime = dateTime.AddSeconds(datePlaceHolderDataItem.DateCalculation.Second.Value);
                }
            }


            if( datePlaceHolderDataItem.Format != null && !string.IsNullOrEmpty(datePlaceHolderDataItem.Format.value) )
            {
                return dateTime.ToString(datePlaceHolderDataItem.Format.value);
            }


            return dateTime.ToString();
        }


using the Object oriented and json (also xml) capability give the developer to design & create generic platform of rule language.

Lets look on the xml :



  command.exe file.xml -s{current_data_time:{"Format":{"value":"MM/dd/yyyy"},"CalcBy":{"Day":{"Value":-30}}}} -e{current_data_time:{"Format":{"value":"MM/dd/yyyy"},"CalcBy":{"Day":{"Value":15}}}}}



Enjoy.