Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#
Article

Using XML CDATA nodes to send files via a Web Service

Rate me:
Please Sign up or sign in to vote.
4.00/5 (9 votes)
26 Jul 2005CPOL2 min read 108.3K   414   24   16
Using XML CDATA nodes to send files via a web service.

Introduction

There was a need to have a web service send a file. The documented way to do this is through WSE (Web Service Extension), which is an add-on to the .NET Framework to do things with web services like attachments. Using two CodeProject articles, I added zipping the file and encoding the file using Base64. Then I placed the encoded file into a CDATA node in an XML document.

Background

As I looked into sending a file via a web service, I was not pleased with the amount of work that had to be done to attach a file using WSE. So I started to look for other options. One of my co-workers suggested using a CDATA node in an XML document. As I looked into the CDATA node, it seemed like it might be a good fit. The CDATA node is for storing blob data in an XML file. It is marked so that it ignores what is between the CDATA nodes. I decided to zip the file to reduce the size of the XML file I would be sending. The problem is that zipping a file produces some characters that cause problems with the XML file. So, that leads us to base64 encoding. I found two code project articles to help me with zipping files with Sharpziplib and base64 encoding that would fit into what I needed.

The code

After having some discussion with my co-workers, we decided on an XML structure that looks like this:

XML
<FILES>
  <FILE FileName="file.txt"><CDATA></CDATA></File>
  <FILE FileName="file2.txt"><CDATA></CDATA></File>
</FILES>

where we could add as many file nodes in the XML document as we wanted. Here is the code to create that XML doc:

C#
public static XmlDocument GetFileXml(String[] Files)
{
  //We are returning an XML Doc
  XmlDocument tmpXmlDoc = new XmlDocument();

  //Create the root node...
  tmpXmlDoc.LoadXml("<FILES></FILES>");

  //Loop through the WorkItemFiles 
  //array and create the file nodes
  for (Int32 i=0;i<=Files.Length-1;i++)
  {
    //The File node
    XmlElement tmpFile = 
          tmpXmlDoc.CreateElement("File");
    //Add the filename attribute to it...
    XmlAttribute tmpA = 
          tmpXmlDoc.CreateAttribute("FileName");
    //Find the last \ to pull off the filename.
    tmpA.Value = 
        Files[i].Substring(Files[i].LastIndexOf(@"\")+1);
    tmpFile.Attributes.Append(tmpA);

    //Read in the File
    Byte[] myCompressed;

    //Compress the file
    myCompressed = 
        CompressionHelper.CompressFile(Files[i]);

        //Next encode the compressed stream 
        //so that we don't blow the 
        //CData node in the XML
    Char[] myEncoded;
    Base64Encoder myBE;
    myBE = new Base64Encoder(myCompressed);
    myEncoded = myBE.GetEncoded();

    //A StringBuilder is the fastest way 
    //to get a string from a byte array
    StringBuilder myStr = new StringBuilder();
    myStr.Append(myEncoded);

    //create CData child appened 
    //to the file node...
    tmpFile.AppendChild(tmpXmlDoc.CreateCDataSection(
                                       myStr.ToString()));

    //Add the file node to the xml doc...
    tmpXmlDoc.DocumentElement.AppendChild(tmpFile);
  } // for

  return tmpXmlDoc;
}

Next, we call the web service to get the XML document. Then, we parse through the XML file and extract and save the documents from the CDATA nodes.

C#
public static void GetFiles(XmlNode inXml, String FilePath)
{
  XmlNodeList myFile;

  myFile = inXml.SelectNodes("/File");

  //This Loops through the File nodes
  for (Int32 j=0;j<=myFile.Count-1;j++)
  {
    //The first child is the CDATA node
    XmlNode myCData = (XmlNode)(myFile.Item(j)).FirstChild;

    //Grab the Filename attribute
    String FileName = 
      ((XmlNode)(myFile.Item(j))).Attributes.Item(0).InnerText;

    //This is the actual data that is in the CDATA
    Char[] theFile;
    theFile = new Char[myCData.InnerText.Length];

    for (Int32 i=0;i<=myCData.InnerText.Length-1;i++)
    {
        theFile[i] = myCData.InnerText[i];

    }//for i
    //Delete the file if it exists
    if (File.Exists(FilePath+FileName))
    {
        File.Delete(FilePath+FileName);
    }

    //next we need to switch from base64 
    //encoding to our zip compressed
    Byte[] myDecoder;
    Base64Decoder myBD = new Base64Decoder(theFile);
    myDecoder = myBD.GetDecoded();

    //Finally we decompress the file and 
    //save it where it is supose to go
    CompressionHelper.DecompressFile(myDecoder,
                                  FilePath+FileName);


  } //for j
}

Conclusion

I would like to thank my co-worker Steve Rowe for his help and guidance in coming to this solution. I would also like to thank Uwe Keim for his shareziplib article and wchvic for his base64 article. Hopefully, others will find this article helpful in their solutions.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
I started my programmer career over 26 years ago doing COBOL and SAS on a MVS mainframe. It didn't take long for me to move into windows programming. I started my windows programming in Delphi (Pascal) with a Microsoft SQL server back end. I started working with vb.net when the beta 2 came out in 2001. After spending most of my programming life as a windows programmer I started to check out asp.net in 2004. I achieved my MCSD.net in April 2005. I have done a lot of MS SQL database stuff. I have a lot of experience with Window Service and Web services as well. I spent three years as a consultant programing in C#. I really enjoyed it and found the switch between vb.net and C# to be mostly syntax. In my current position I am programming in C# working on WPF and MSSql database stuff. Lately I have been using VS2019.

On a personal note I am a born again Christian, if anyone has any questions about what it means to have a right relationship with God or if you have questions about who Jesus Christ is, send me an e-mail. ben.kubicek[at]netzero[dot]com You need to replace the [at] with @ and [dot] with . for the email to work. My relationship with God gives purpose and meaning to my life.

Comments and Discussions

 
Questionreference.cs Pin
impeesa_016-Jan-09 16:30
professionalimpeesa_016-Jan-09 16:30 
AnswerRe: reference.cs Pin
kubben7-Jan-09 2:32
kubben7-Jan-09 2:32 
Generalfedex web service with axapta Pin
adsfdsdsfds14-Nov-08 9:18
adsfdsdsfds14-Nov-08 9:18 
GeneralRe: fedex web service with axapta Pin
kubben14-Nov-08 9:21
kubben14-Nov-08 9:21 
QuestionHow diffirence Pin
HoanglkKHTN17-Jul-06 22:46
HoanglkKHTN17-Jul-06 22:46 
AnswerRe: How diffirence Pin
kubben18-Jul-06 2:02
kubben18-Jul-06 2:02 
GeneralFile progressbar Pin
logikonline6-Aug-05 2:14
logikonline6-Aug-05 2:14 
GeneralRe: File progressbar Pin
kubben6-Aug-05 2:43
kubben6-Aug-05 2:43 
Thanks for you question. Actually, I think it is possible. Instead of using actual files if you use a Stream, the only way to zip the stream is to put it in an array of bytes. This is what we are actually doing at work. Anyway, the fastest way is just to create a byte array that is the size of the stream length, but if you wanted to keep track of the progress, you could look at the size of the stream array divid it by how many bars you want on your progress bar. NOTE the down side to this is that you have to use the same size block on the other end otherwise you won't be un-ziping the correct block of data. So it would be kind of a pain and it would be slower.

Another options using the existing code... There are kind of eight status...

Windows app requests file.
Web service has\finds the file.
Web service zips the file.
Web service encodes the file.
Web service puts encoded file in xml doc
Windows app receives xml doc.
Windows app decodes file
windows app un-zips file and saves it.

Anyway, hope that was helpful.

Ben
Generalw3c, WS-* Pin
ian mariano3-Aug-05 3:04
ian mariano3-Aug-05 3:04 
GeneralRe: w3c, WS-* Pin
kubben3-Aug-05 8:21
kubben3-Aug-05 8:21 
GeneralWSE/DIME solution Pin
..Hubert..2-Aug-05 21:45
..Hubert..2-Aug-05 21:45 
GeneralRe: WSE/DIME solution Pin
kubben3-Aug-05 8:17
kubben3-Aug-05 8:17 
GeneralRe: WSE/DIME solution Pin
..Hubert..3-Aug-05 21:02
..Hubert..3-Aug-05 21:02 
GeneralRe: WSE/DIME solution Pin
kubben4-Aug-05 3:32
kubben4-Aug-05 3:32 
GeneralAttention!!!!! Pin
ilnar2-Aug-05 19:29
ilnar2-Aug-05 19:29 
GeneralRe: Attention!!!!! Pin
kubben3-Aug-05 8:19
kubben3-Aug-05 8:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.