How to create custom output based on custom XML?
-
Tuesday, March 05, 2013 10:59 PM
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
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
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.
- Marked As Answer by Naomi NMicrosoft Community Contributor Wednesday, March 06, 2013 9:43 PM
-
Wednesday, March 06, 2013 12:45 PM
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 PMDo 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
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 ...

