Answered How to create custom output based on custom XML?

  • Tuesday, March 05, 2013 10:59 PM
     
      Has Code

    Hi everybody,

    I have the following text which looks like XML without root node:

    String cInput = @"<Changed>
    	<guests>
    	</guests>
    </Changed>
    
    <CurrentState>
    <guests><guest_no>5000001</guest_no><parent_no>5000001</parent_no>
    <addit_no>0</addit_no><addit_no2>0</addit_no2><trans_no>0</trans_no>
    <guestgroup></guestgroup><salute></salute><first_name>MEC</first_name>
    <last_name>LAR</last_name><mid_name></mid_name><suffix></suffix><e_mail></e_mail>
    <birth_date>01/20/1966 12:00:00 AM</birth_date><gender></gender>
    <mug_shot></mug_shot><notes></notes><e_message></e_message>
    <number_2>0</number_2>
    <number_3>0</number_3><number_4>.00</number_4><number_5>.00</number_5>
    <text_1></text_1><text_2></text_2><text_3></text_3><text_4></text_4><text_5></text_5><text_6>
    </text_6><text_7></text_7><text_8></text_8><memo_1></memo_1><memo_2></memo_2><memo_3></memo_3>
    <web_user></web_user><web_pswd></web_pswd><importid>SIMP5000001</importid>
    <import2nd>S2ND5000001</import2nd><importir>SIR5000001</importir>
    <importeml>SEML5000001</importeml><guest_2nd>0</guest_2nd><salute2></salute2>
    <firstname2>JIM</firstname2><lastname2>LAR</lastname2>
    <midname2></midname2><suffix2></suffix2>
    <salespoint>LISA</salespoint><operator>ADMIN</operator>
    <date_time>03/17/2008 03:59:39 PM</date_time>
    <last_mod>6</last_mod><gfwdstatus>1</gfwdstatus>
    <guest_id>0</guest_id><relation>0</relation>
    <role_no>0</role_no><acct_name></acct_name>
    <vipcode>0</vipcode><height>0</height>
    <weight>0</weight><height_m>0</height_m>
    <weight_m>0</weight_m><no_mail>false</no_mail><no_email>false</no_email>
    <no_phone>false</no_phone><addr_pref>0</addr_pref></guests></CurrentState>";

    Based on this or similar input I want to generate the following:

    String expected = @"Changed:       
    
    CurrentState:  
     guests:        
      guest_no:      5000001
      parent_no:     5000001
      first_name:    MEC
      last_name:     LAR
      birth_date:    01/20/1966 12:00:00 AM
      importid:      SIMP5000001
      import2nd:     S2ND5000001
      importir:      SIR5000001
      importeml:     SEML5000001
      firstname2:    JIM
      lastname2:     LAR
      salespoint:    LISA
      operator:      ADMIN
      date_time:     03/17/2008 03:59:39 PM
      last_mod:      6
      gfwdstatus:    1";

    So, the idea is to don't output empty elements (number = 0, false, empty strings) and also keep indentation equal to the nesting level of the tag.

    My XML knowledge is limited, I tried to look in MS documentation and samples but could not find anything helpful. This is what I started with and will appreciate some help:

     
    /// <summary>
          /// Formats XML into a string
          /// </summary>
          /// <param name="input"></param>
          /// <param name="level"></param>
          /// <returns></returns>
          public String XmlDisplay(String input, Int32 level = 0)
          {
             try
             {
                if (0 == level)
                {
                   String rootNode = "DummyNode";
                   input = String.Format("<{0}>{1}</{0}>", rootNode, input);
                }
                if (level > 5)
                   return ""; // don't create infinite loop
    
                XElement elements = XElement.Parse(input);
                StringBuilder output = new StringBuilder();
                String elementValue, tagName;
                String result;
                XmlNodeType elementType;
                foreach (XElement xe in elements.Elements())
                {
                   tagName = xe.Name.ToString();
                   foreach (XNode xn in xe.Nodes())
                   {
                     elementType = xn.NodeType;
                     
                     if (elementType == XmlNodeType.Text)
                     {
                        elementValue = xn.ToString();                    
                        
                        if (!String.IsNullOrEmpty(elementValue) && "0" != elementValue && "FALSE" !=elementValue.ToUpper())
                        {
                           if ("CC_SWIPE" == tagName.ToUpper())
                           {
                              Int32 cardLength = elementValue.Length;
                              String mask = new String('X', cardLength - 4);
                              String maskedCardNumber = String.Format("{0}{1}", mask, elementValue.Substring(cardLength - 4));
                              result = "".PadRight(level) + (tagName + ":").PadRight(15) + maskedCardNumber;
                           }
                           else
                           {
                              if (200 > elementValue.Length)
                                 elementValue = "Too long to display";
                              result = "".PadRight(level) + (tagName + ":").PadRight(15) + elementValue;
                           }
                        }
                     }
                     else
                     {
    
                        result = XmlDisplay(xn.ToString(), level + 1);
                        if (!String.IsNullOrEmpty(result))
                           output.AppendLine(result);
                     }
                   }               
                }
                return output.ToString();
             }
             catch (Exception ex)
             {
                String error = ex.Message.ToString();
                Logging.Log(error, 1);
                return String.Format("<ERR>0</ERR><STATUSCODE>100</STATUSCODE><MSG>{0}</MSG>", error);
             }
             
          }


    For every expert, there is an equal and opposite expert. - Becker's Law


    My blog

All Replies

  • Wednesday, March 06, 2013 5:34 AM
     
      Has Code

    I think I've been able to create the desired output myself although I'll still appreciate suggestions of improving the code

          /// <summary>
          /// Formats a single element
          /// </summary>
          /// <param name="xe"></param>
          /// <param name="level"></param>
          /// <returns></returns>
          public String FormatSingleXmlElement(XElement xe, Int32 level = 0)
          {
             String result = "";
             String elementValue = "";
             String tagName = xe.Name.ToString();
             if (!xe.IsEmpty)
             {
                elementValue = xe.Value;
    
                if (!elementValue.IsValueEmpty())
                {
                   if ("CC_SWIPE" == tagName.ToUpper())
                   {
                      {
                         Int32 cardLength = elementValue.Length;
                         String mask = new String('X', cardLength - 4);
                         String maskedCardNumber = String.Format("{0}{1}", mask, elementValue.Substring(cardLength - 4));
                         result = "".PadRight(level) + (tagName + ":").PadRight(15) + maskedCardNumber;
                      }
                   }
                   else
                   {
                      if (200 < elementValue.Length)
                         elementValue = elementValue.Left(200) + "...";
                      result = "".PadRight(level) + (tagName + ":").PadRight(15) + elementValue;
                   }
                }
             }
             return result;
          }
    
          /// <summary>
          /// Based on the element either processes the current element or uses recursion
          /// </summary>
          /// <param name="element"></param>
          /// <param name="level"></param>
          /// <returns></returns>
          public String XmlToString(XElement element, Int32 level = 0)
          {
             StringBuilder output = new StringBuilder();
             String tagName = element.Name.ToString();
             String result = "";
             if (element.HasElements)
             {
                output.AppendLine("".PadRight(level) + (tagName + ":").PadRight(15));
                foreach (XNode xn in element.Nodes())
                {
                   result = XmlToString((XElement)xn, level + 1);  
                   if (!String.IsNullOrWhiteSpace(result))
                      output.AppendLine(result);
                }
             }
             else
             {
                return FormatSingleXmlElement(element, level);
             }
    
             return output.ToString();
          }
    
          /// <summary>
          /// Formats XML into a string
          /// </summary>
          /// <param name="input"></param>
          /// <param name="level"></param>
          /// <returns></returns>
          public String XmlDisplay(String input, Int32 level = 0)
          {
             try
             {
                if (0 == level)
                {
                   String rootNode = "DummyNode";
                   input = String.Format("<{0}>{1}</{0}>", rootNode, input);
                }
                if (level > 5)
                   return ""; // don't create infinite loop
    
                XElement elements = XElement.Parse(input);
                StringBuilder output = new StringBuilder();
           
                foreach (XElement xe in elements.Elements())
                {
                   output.AppendLine(XmlToString(xe, level));
                }
                return output.ToString().TrimEnd('\r', '\n'); 
             }
             catch (Exception ex)
             {
                String error = ex.Message.ToString();
                Logging.Log(error, 1);
                return String.Format("<ERR>0</ERR><STATUSCODE>100</STATUSCODE><MSG>{0}</MSG>", error);
             }
          }
    


    For every expert, there is an equal and opposite expert. - Becker's Law


    My blog

  • Wednesday, March 06, 2013 12:37 PM
     
     Answered Has Code

    Maybe something like

    public String XmlDisplay(String input)
    {
        String rootNode = "DummyNode";
        input = String.Format("<{0}>{1}</{0}>", rootNode, input);
    
        XDocument doc = XDocument.Parse(input);
    
        var nodes = doc.Element(rootNode).Descendants()
            .Where(n => n.Value != "0" && n.Value != ".00" && n.Value != "false" && n.Value != "")
            .Select(n => new { n.Name, n.Value, Count = n.Ancestors().Count() - 1, n.HasElements });
    
        var output = new StringBuilder();
    
        foreach (var node in nodes)
        {
            if (node.HasElements)
                output.AppendLine(new string(' ', node.Count) + node.Name + ":");
            else
                output.AppendLine(new string(' ', node.Count) + node.Name + "\t" + node.Value);
        }
        return output.ToString();
    }

    Ancestors().Count() using for indentation.

  • Wednesday, March 06, 2013 12:45 PM
     
      Has Code

    If you definitely need to keep Changed: in the output then

    .Where(n => n.Value != "0" && n.Value != ".00" && n.Value != "false" && n.Value != "" || n.HasElements)

  • Wednesday, March 06, 2013 9:51 PM
     
     
    Do you know what can I adjust in the code above to be able to insert one blank line after Changed? In this particular case we have 1 empty element and we want to add a blank line for it.

    For every expert, there is an equal and opposite expert. - Becker's Law


    My blog

  • Thursday, March 07, 2013 9:52 AM
     
      Has Code

    In the foreach loop

    if (node.HasElements)
    {
        output.AppendLine(new string(' ', node.Count) + node.Name + ":");
        if (node.Value == "")
            output.AppendLine(); // add blank line, in case we have no any value
    }
    else
        ...