1
\$\begingroup\$

I need to receive an XML post then process all the data, and do whatever I need to do with the data. I could be receiving anywhere from 60 to 100 values. I know what all the elements are going to be, and all except one is mandatory. For billing, it could either be credit card info or checking info, so I need to check the first child's name to see what the billing type is.

Here is a cut-down example:

<XmlData>
    <FirstName>FirstName</FirstName>
    <LastName>LastName</LastName>
    <Address>666 S st.</Address>
    <Email>email@email.com</Email>
    <City>Beverly Hills</City>
    <Zip>90210</Zip>
    <PhoneNumber>5555555555</PhoneNumber>
    <!-- 14more nodes -->
    <Billing> 
        <CCType>Visa</CCType>
        <CCNumber>55554443332221</CCNumber>
        <! -- 8 More Nodes for billing -->
    </Billing>
</XmlData>

Now, here is how I am handling processing the data. In my Page_Load, I am first calling a method that will verify the XML is well formatted and not null(not posting this code). Next, I call a method that will read all the data and assign it private fields using LINQ to XML.

private string firstName = string.Empty;
private string lastName = string.Empty;
private string address = string.Empty;
private string email= string.Empty;
private string city= string.Empty;
private string zip = string.Empty;
private Int64 phoneNumber;
private string billingMethod = string.Empty;
private string ccType = string.Empty;
private Int64 ccNumber;


private void ReadXml(){

            using (StringReader sr = new StringReader(payload)) {
                xd = XDocument.Load(sr);
            }
            var node = xd.Root;
            //lets just make sure all the nodes we need are present, then continue
            //this is where billingMethod is assigned; after validating the Billing
            //node is present, it will check if I am getting credit card info or check                                                             
            //info, then assign "credit" or "check" to billingMethod
            ValidateNeededNodesPresent(node);
            if (errors.Count > 0) {
                Response();
                return;
            }
            //verify the nodes are not null, and assign them
            firstName= VerifyString(node.Element(XmlKey.FirstName).Value, XmlKey.FirstName);
            lastName= VerifyString(node.Element(XmlKey.LastName).Value, XmlKey.LastName);
            zip = ValidateStringMax(node.Element(XmlKey.Zip).Value, XmlKey.Zip, 5);
            email = VerifyString(node.Element(XmlKey.Email).Value, XmlKey.Email);
            address= VerifyString(node.Element(XmlKey.Address).Value, XmlKey.Address);
            city= ValidateData(node.Element(XmlKey.City).Value, XmlKey.City);
            phoneNumber = VerifyInt64(node.Element(XmlKey.PhoneNumber).Value, XmlKey.PhoneNumber);

            if(billingMethod == "credit")
            {
                ccNumber = VerifyInt64(node.Element(XmlKey.Billing).Element(XmlKey.CCNumber).Value,                                      XmlKey.CCdNumber);
                ccType = VerifyString(
                    node.Element(XmlKey.Billing).Element(XmlKey.CCType).Value, XmlKey.CCType);

            }

}

Now, here is my list of verify classes that check to make sure the data is not null and in the correct format (int, int64, DateTime, etc.). If it is not in the correct format, it will add an error to errors. The way is grabs errors is by getting the error string from a dictionary, where the key is the XML node name, and the value is the error message:

public Int64 VerifyInt64(string data, string element)
        {
            Int64 number = 0L;
            if (!Int64.TryParse(data, out number) || String.IsNullOrEmpty(data))
            {
                errors.Add(ErrorDictionary.Errors[element]);

            }
            return number;

        }

public string VerifyString(string data, string element)
        {
             if (String.IsNullOrEmpty(data) || String.IsNullOrEmpty(data))
        {
            errorMessages.Add(ErrorDictionary.Errors[element]);
        }
        return data;
        }

public string VerifyStringMax(string data, string element, int lengthMax)
        {
            if (data.Length > lengthMax || String.IsNullOrEmpty(data))
            {
                errors.Add(ErrorDictionary.Errors[element]);

            }
            return data;
        }

The code works fine, but I cant help but think that I did more than I needed to. I'm strictly speaking about how I'm reading the XML, validating data, and assigning values in this case.

Is this code efficient? Is there a better way I can go about reading XML data and assigning values while validating the data?

\$\endgroup\$
2
\$\begingroup\$

If your main goal is to verify the document structure, you may try using an XSD. Once you have a schema doc defined, you can use some LINQ to XML extensions to perform the validation and get the error messages (if any) that result. Additionally, you can use XSD to define default values for missing elements/attributes.

Once you do that, the code can be simplified:

        firstName= (string) node.Element(XmlKey.FirstName);
        lastName= (string) node.Element(XmlKey.LastName);
        zip = (string) node.Element(XmlKey.Zip);
        email = (string) node.Element(XmlKey.Email);
        address= (string) node.Element(XmlKey.Address);
        city= (string) node.Element(XmlKey.City);
        phoneNumber = (long) node.Element(XmlKey.PhoneNumber);

        if(billingMethod == "credit")
        {
            var  billingElement = node.Element(XmlKey.Billing);
            ccNumber = (long) billingElement.Element(XmlKey.CCNumber);
            ccType = (string) billingElement.Element(XmlKey.CCType);
        }

Though, to be honest, this feels like you would save a lot of work having a data object for a user that can be serialized/deserialized via a DataContractSerializer rather than hand-building and parsing.

\$\endgroup\$
  • \$\begingroup\$ Unfortunately I am getting the XML from a 3rd party, so having any XML with XSD is out of the question. \$\endgroup\$ – Chris Mar 12 '12 at 19:57
  • \$\begingroup\$ @Chris Why? You don't need to store the XSD definition in the XML file. See this StackOverflow question. \$\endgroup\$ – Quentin Pradet Mar 12 '12 at 20:45
  • \$\begingroup\$ You may note that the example provided within the first link does not put any XSD reference in the XML that is validated. While it is nice to have a reference to the XSD within the XML (when using the document in XML editors), it is not required for the LINQ to XML validation to work. \$\endgroup\$ – Dan Lyons Mar 23 '12 at 17:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.