Solved

C#: Building XML file

Posted on 2015-01-06
13
475 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
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 33

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
DevOps Toolchain Recommendations

Read this Gartner Research Note and discover how your IT organization can automate and optimize DevOps processes using a toolchain architecture.

 
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 29

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 29

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 29

Assisted Solution

by:anarki_jimbel
anarki_jimbel earned 500 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 29

Expert Comment

by:anarki_jimbel
ID: 40534747
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
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 29

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

Master Your Team's Linux and Cloud Stack!

The average business loses $13.5M per year to ineffective training (per 1,000 employees). Keep ahead of the competition and combine in-person quality with online cost and flexibility by training with Linux Academy.

Question has a verified solution.

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

Suggested Solutions

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…
Performance in games development is paramount: every microsecond counts to be able to do everything in less than 33ms (aiming at 16ms). C# foreach statement is one of the worst performance killers, and here I explain why.
This Micro Tutorial will give you a basic overview how to record your screen with Microsoft Expression Encoder. This program is still free and open for the public to download. This will be demonstrated using Microsoft Expression Encoder 4.
Email security requires an ever evolving service that stays up to date with counter-evolving threats. The Email Laundry perform Research and Development to ensure their email security service evolves faster than cyber criminals. We apply our Threat…

810 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