Solved

C#: Building XML file

Posted on 2015-01-06
13
489 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 5
13 Comments
 
LVL 30

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
Creating Instructional Tutorials  

For Any Use & On Any Platform

Contextual Guidance at the moment of need helps your employees/users adopt software o& achieve even the most complex tasks instantly. Boost knowledge retention, software adoption & employee engagement with easy solution.

 
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 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 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 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 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

Space-Age Communications Transitions to DevOps

ViaSat, a global provider of satellite and wireless communications, securely connects businesses, governments, and organizations to the Internet. Learn how ViaSat’s Network Solutions Engineer, drove the transition from a traditional network support to a DevOps-centric model.

Question has a verified solution.

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

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!
I've attached the XLSM Excel spreadsheet I used in the video and also text files containing the macros used below. https://filedb.experts-exchange.com/incoming/2017/03_w12/1151775/Permutations.txt https://filedb.experts-exchange.com/incoming/201…
How to Install VMware Tools in Red Hat Enterprise Linux 6.4 (RHEL 6.4) Step-by-Step Tutorial

726 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