?
Solved

C#: Building XML file

Posted on 2015-01-06
13
Medium Priority
?
572 Views
Last Modified: 2015-01-06
I am new to XML. A client requested to have our web application (C#) to generate an XML file. They gave us a sample XML file to use as a template (attached). We also have a stored procedure that returns the data needed to populate SOME of the fields in the XML file. sample.xml

I looked through some of the existing XML projects in our application (below) to hopefully get me started, but no luck.
XmlDeclaration xmldecl;
xmldecl = doc.CreateXmlDeclaration("1.0", "utf-8", null);
XmlElement root = doc.DocumentElement;
doc.InsertBefore(xmldecl, root);
...
...
...

What is the best way to generate the attached XML file using C#?

Can I do the following?
string xmlString = @"
<?xml version="1.0" encoding="UTF-8"?>
	<collectpay-payment-request xmlns="http://www.domain.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.domain.com ">
		<payment-batch-request>"

Open in new window

0
Comment
Question by:pzozulka
  • 7
  • 5
13 Comments
 
LVL 30

Accepted Solution

by:
anarki_jimbel earned 2000 total points
ID: 40534311
No, never build an xml as a string. This is quite bad practice, and sometimes dangerous.

Try the following code.

You'll see - it's pretty easy to build xml using xml document.
 - you create a document,
- the you create a "root" element (you may have ONE only)
- then you create other elements and attach them to the root or to each other (depends on hierarchy).
- save (or print or send etc.)

 
using System.Xml;

...
       private void button1_Click(object sender, EventArgs e)
        {
            XmlDocument doc = new XmlDocument();

            // document element
            XmlElement cppr = doc.CreateElement("collectpay-payment-request");
            doc.AppendChild(cppr);

            // xml declaration
            XmlDeclaration xmlDeclaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
            XmlElement root = doc.DocumentElement;
            doc.InsertBefore(xmlDeclaration, root);

            // namespace
            root.SetAttribute("xmlns", "http://www.domain.com");
            root.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
            root.SetAttribute("xsi", "http://www.domain.com");



            XmlElement pbr = doc.CreateElement("payment-batch-request");
            cppr.AppendChild(pbr);
            XmlElement bid = doc.CreateElement("business-id");
            //set element text to something you probably get from database, e.g., 1234
            bid.InnerText = "1234";
            pbr.AppendChild(bid);
            XmlElement prs = doc.CreateElement("payment-requests");
            pbr.AppendChild(prs);
            XmlElement bpr = doc.CreateElement("bank-payment-request");
            prs.AppendChild(bpr);
            XmlElement dbid = doc.CreateElement("division-business-id");
            //set element value to something you probably get from database, e.g., A777
            dbid.InnerText = "A777";
            bpr.AppendChild(dbid);          
            // Add bank account stuff to create the following structure:
            //<bank-account>
            //   <bank-account-type></bank-account-type>
            //   <routing-number></routing-number>
            //   <bank-account-number></bank-account-number>
            //   <account-holder-name></account-holder-name>
            //   <account-address-1 />
            //   <account-city />
            //   <account-state />
            //   <account-postal-code />
            //   <account-country-code></account-country-code>
            //</bank-account>
            XmlElement ba = doc.CreateElement("bank-account");
            bpr.AppendChild(ba);
            XmlElement bat = doc.CreateElement("bank-account-type");
            bat.InnerText = "cheque"; // from DB
            ba.AppendChild(bat);
            XmlElement rn = doc.CreateElement("routing-number");
            rn.InnerText = "333"; // from DB
            ba.AppendChild(rn);
            XmlElement ban = doc.CreateElement("bank-account-number");
            ban.InnerText = "012-987654321-00"; // from DB
            ba.AppendChild(ban);
            XmlElement ahn = doc.CreateElement("account-holder-name");
            ahn.InnerText = "John Doe"; // from DB
            ba.AppendChild(ahn);
            XmlElement aa1 = doc.CreateElement("account-address-1");
            aa1.InnerText = "1 Nowhere Street"; // from DB
            ba.AppendChild(aa1);
            XmlElement ac = doc.CreateElement("account-city");
            ac.InnerText = "New York"; // from DB
            ba.AppendChild(ac);
            XmlElement ast = doc.CreateElement("account-state");
            ast.InnerText = "New York"; // from DB
            ba.AppendChild(ast);
            XmlElement apc = doc.CreateElement("account-postal-code");
            apc.InnerText = "2222"; // from DB
            ba.AppendChild(apc);
            XmlElement acc = doc.CreateElement("account-country-code");
            acc.InnerText = "+1"; // from DB
            ba.AppendChild(acc); 

            // now nex element one level up:
            XmlElement nsec = doc.CreateElement("nacha-standard-entry-class");
            nsec.InnerText = @"Don't know what's that"; // from DB
            bpr.AppendChild(nsec); 

            // etc. - keep adding elements
                   

            doc.Save(@"C:\work\mytestxml.xml");

        }

Open in new window



Output:

<?xml version="1.0" encoding="UTF-8"?>
<collectpay-payment-request xmlns="http://www.domain.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi="http://www.domain.com">
  <payment-batch-request>
    <business-id>1234</business-id>
    <payment-requests>
      <bank-payment-request>
        <division-business-id>A777</division-business-id>
        <bank-account>
          <bank-account-type>cheque</bank-account-type>
          <routing-number>333</routing-number>
          <bank-account-number>012-987654321-00</bank-account-number>
          <account-holder-name>John Doe</account-holder-name>
          <account-address-1>1 Nowhere Street</account-address-1>
          <account-city>New York</account-city>
          <account-state>New York</account-state>
          <account-postal-code>2222</account-postal-code>
          <account-country-code>+1</account-country-code>
        </bank-account>
        <nacha-standard-entry-class>Don't know what's that</nacha-standard-entry-class>
      </bank-payment-request>
    </payment-requests>
  </payment-batch-request>
</collectpay-payment-request>

Open in new window

0
 
LVL 8

Author Comment

by:pzozulka
ID: 40534458
This all looks great except one thing. On line 20 in your code above, I am getting the following XmlException:

The ':' character, hexadecimal value 0x3A, cannot be included in a name.
0
 
LVL 35

Expert Comment

by:it_saige
ID: 40534519
And yet another method, simply serialize a class that represents your object returned from the database, i.e. (sample to get you going) -
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace EE_Q28591576
{
	class Program
	{
		static void Main(string[] args)
		{
			PaymentRequest pRequest = new PaymentRequest();
			pRequest.BankRequests.Add(new BankPaymentRequest() { BusinessID = "division business id" });
			pRequest.CreditCardRequests.Add(new CreditCardPaymentRequest() { BusinessID = "division business id" });
			PaymentBatchRequest pbRequest = new PaymentBatchRequest() { BusinessID = "business id" };
			pbRequest.Requests.Add(pRequest);
			CollectPayPaymentRequest request = new CollectPayPaymentRequest();
			request.Batch.Add(pbRequest);
			request.GenerateXml("text.xml");
		}
	}

	[Serializable(), XmlRoot("collectpay-payment-request", Namespace = BASE_NAMESPACE)]
	public class CollectPayPaymentRequest
	{
		private const string BASE_NAMESPACE = "http://www.domain.com";
		private const string XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance";

		private readonly XmlSerializerNamespaces fNamespaces = new XmlSerializerNamespaces(new[] { new XmlQualifiedName(string.Empty, BASE_NAMESPACE), new XmlQualifiedName("xsi", XSI_NAMESPACE) });
		private readonly string fSchemaLocation = BASE_NAMESPACE;
		private readonly List<PaymentBatchRequest> fBatch = new List<PaymentBatchRequest>();

		[XmlNamespaceDeclarations]
		public XmlSerializerNamespaces Namespaces { get { return fNamespaces; } }

		[XmlAttribute(AttributeName = "schemaLocation", Namespace = XSI_NAMESPACE)]
		public string SchemaLocation { get { return fSchemaLocation; } }

		[XmlElement("payment-batch-request")]
		public List<PaymentBatchRequest> Batch { get { return fBatch; } }

		public void GenerateXml(string fileName)
		{
			GenerateXml(new FileInfo(fileName));
		}

		public void GenerateXml(FileInfo file)
		{
			XmlSerializer serializer = new XmlSerializer(typeof(CollectPayPaymentRequest));
			using (TextWriter writer = new StreamWriter(file.FullName))
				serializer.Serialize(writer, this);
		}
	}

	[Serializable()]
	public class PaymentBatchRequest
	{
		private readonly List<PaymentRequest> fRequests = new List<PaymentRequest>();

		[XmlElement("business-id")]
		public string BusinessID { get; set; }

		[XmlElement("payment-requests")]
		public List<PaymentRequest> Requests { get { return fRequests; } }
	}

	[Serializable()]
	public class PaymentRequest
	{
		private readonly List<BankPaymentRequest> fBankRequests = new List<BankPaymentRequest>();
		private readonly List<CreditCardPaymentRequest> fCreditCardRequests = new List<CreditCardPaymentRequest>();

		[XmlElement("bank-payment-requests")]
		public List<BankPaymentRequest> BankRequests { get { return fBankRequests; } }

		[XmlElement("credit-card-payment-requests")]
		public List<CreditCardPaymentRequest> CreditCardRequests { get { return fCreditCardRequests; } }
	}

	[Serializable()]
	public class BankPaymentRequest
	{
		[XmlElement("division-business-id")]
		public string BusinessID { get; set; }
	}

	[Serializable()]
	public class CreditCardPaymentRequest
	{
		[XmlElement("division-business-id")]
		public string BusinessID { get; set; }
	}
}

Open in new window

Produces the following output -Capture.JPGXml contains -Capture.JPG-saige-
0
 [eBook] Windows Nano Server

Download this FREE eBook and learn all you need to get started with Windows Nano Server, including deployment options, remote management
and troubleshooting tips and tricks

 
LVL 8

Author Comment

by:pzozulka
ID: 40534581
it_saige:

Thanks, but I am already deep into a blend of anarki_jimbel's recommendation, and another way I found called XML to LINQ. However, I'm still stuck on the XmlException above.

I'm trying various ways, but still no luck, hoping someone can help:

Here's what I got:

XNamespace collectpay = "http://www.domain.com";
XNamespace xmlSchema = "http://www.w3.org/2001/XMLSchema-instance";

XDocument doc = new XDocument(
	new XElement("collectpay-payment-request",
			new XAttribute("xmlns", collectpay),
			new XAttribute(XNamespace.Xmlns + "xsi", xmlSchema),
			new XAttribute(XNamespace.Xmlns + "schemaLocation", collectpay),
		new XElement("payment-batch-request",
			new XElement("business-id", BUSINESS_ID.ToString()),

Open in new window


I am no longer getting the error above, but now getting a new error message:
The prefix '' cannot be redefined from '' to 'http://www.domain.com' within the same start element tag.
0
 
LVL 8

Author Comment

by:pzozulka
ID: 40534585
Am I not creating the XAttributes correctly?
0
 
LVL 30

Expert Comment

by:anarki_jimbel
ID: 40534600
OK, you want use LINQ...

Need to check. Because this should not happen for XmlDocument. This is common approach.
Need some time :).
0
 
LVL 30

Expert Comment

by:anarki_jimbel
ID: 40534707
Try this:

            XNamespace blank = @"http://www.domain.com";
            XNamespace xsi = @"http://www.w3.org/2001/XMLSchema-instance";
            XNamespace schemaLocation = @"http://www.domain.com";

            XDocument doc = new XDocument(
	            new XElement(blank + "collectpay-payment-request",
			        new XAttribute("xmlns", blank),
                    new XAttribute(XNamespace.Xmlns + "xsi", xsi),
                    new XAttribute(xsi + "schemaLocation", schemaLocation),
		            new XElement(blank + "payment-batch-request",
			            new XElement("business-id", "business ID"),
                        new XElement("payment-requests",
                            new XElement("bank-payment-request",
                                new XElement("divizion-business-id", "A777"),
                                new XElement("bank-account",
                                    new XElement("bank-account-type", "cheque")
                                )
                            )
                        )
                    )
                )
            );


            doc.Save(@"C:\work\x_xml.xml");

Open in new window

0
 
LVL 30

Assisted Solution

by:anarki_jimbel
anarki_jimbel earned 2000 total points
ID: 40534722
OK, try this, You may need to add "blank" namespace to each  element. Pretty confusing...

            XNamespace blank = @"http://www.domain.com";
            XNamespace xsi = @"http://www.w3.org/2001/XMLSchema-instance";
            XNamespace schemaLocation = @"http://www.domain.com";

            XDocument doc = new XDocument(
	            new XElement(blank + "collectpay-payment-request",
			        new XAttribute("xmlns", blank),
                    new XAttribute(XNamespace.Xmlns + "xsi", xsi),
                    new XAttribute(xsi + "schemaLocation", schemaLocation),
		            new XElement(blank + "payment-batch-request",
                        new XElement(blank + "business-id", "business ID"),
                        new XElement(blank + "payment-requests",
                            new XElement(blank + "bank-payment-request",
                                new XElement(blank + "divizion-business-id", "A777"),
                                new XElement(blank + "bank-account",
                                    new XElement(blank + "bank-account-type", "cheque")
                                )
                            )
                        )
                    )
                )
            );


            doc.Save(@"C:\work\x_xml.xml");

Open in new window

0
 
LVL 8

Author Comment

by:pzozulka
ID: 40534738
Messy...it works, but feels messy. I'll try to implement and see if this gets past the QA dept.

:) Hopefully there's a better solution to eliminate the blank...
0
 
LVL 30

Expert Comment

by:anarki_jimbel
ID: 40534747
QA department looks at code?! :)

Adding a namespace to nested XElements is not MY solution :).
0
 
LVL 30

Assisted Solution

by:anarki_jimbel
anarki_jimbel earned 2000 total points
ID: 40534751
OK, have a look at MSDN article below.
Hopefully your QA will be satisfied :D

http://msdn.microsoft.com/en-nz/library/bb387075.aspx



// Create an XML tree in a namespace.
XNamespace aw = "http://www.adventure-works.com";
XElement root = new XElement(aw + "Root",
    new XElement(aw + "Child", "child content")
);

0
 
LVL 8

Author Comment

by:pzozulka
ID: 40534760
Yea, since I'm the new Dev, QA management still does code review on me. :)

Wow, what a price to pay for adding attributes to a single line of XML code. I have nearly 60 child nodes.

Either way, thanks for your help.
0
 
LVL 30

Expert Comment

by:anarki_jimbel
ID: 40534793
Really, you have chosen the right approach.
With XmlDocument (my initial example - I'm a bit more comfortable with XmlDocument :))  -
you would have much more work :).
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Many times as a report developer I've been asked to display normalized data such as three rows with values Jack, Joe, and Bob as a single comma-separated string such as 'Jack, Joe, Bob', and vice versa.  Here's how to do it. 
Real-time is more about the business, not the technology. In day-to-day life, to make real-time decisions like buying or investing, business needs the latest information(e.g. Gold Rate/Stock Rate). Unlike traditional days, you need not wait for a fe…
this video summaries big data hadoop online training demo (http://onlineitguru.com/big-data-hadoop-online-training-placement.html) , and covers basics in big data hadoop .
Integration Management Part 2
Suggested Courses
Course of the Month15 days, 16 hours left to enroll

850 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question