Solved

C#: Building XML file

Posted on 2015-01-06
13
450 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 29

Accepted Solution

by:
anarki_jimbel earned 500 total points
Comment Utility
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
Comment Utility
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 32

Expert Comment

by:it_saige
Comment Utility
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
 
LVL 8

Author Comment

by:pzozulka
Comment Utility
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
Comment Utility
Am I not creating the XAttributes correctly?
0
 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
OK, you want use LINQ...

Need to check. Because this should not happen for XmlDocument. This is common approach.
Need some time :).
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 29

Expert Comment

by:anarki_jimbel
Comment Utility
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 29

Assisted Solution

by:anarki_jimbel
anarki_jimbel earned 500 total points
Comment Utility
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
Comment Utility
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 29

Expert Comment

by:anarki_jimbel
Comment Utility
QA department looks at code?! :)

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

Assisted Solution

by:anarki_jimbel
anarki_jimbel earned 500 total points
Comment Utility
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
Comment Utility
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 29

Expert Comment

by:anarki_jimbel
Comment Utility
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

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Introduction In my previous article (http://www.experts-exchange.com/Microsoft/Development/MS-SQL-Server/SSIS/A_9150-Loading-XML-Using-SSIS.html) I showed you how the XML Source component can be used to load XML files into a SQL Server database, us…
Entity Framework is a powerful tool to help you interact with the DataBase but still doesn't help much when we have a Stored Procedure that returns more than one resultset. The solution takes some of out-of-the-box thinking; read on!
This video discusses moving either the default database or any database to a new volume.
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

743 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

Need Help in Real-Time?

Connect with top rated Experts

14 Experts available now in Live!

Get 1:1 Help Now