Scott Hanselman

XmlFragmentWriter - Omiting the Xml Declaration and the XSD and XSI namespaces

August 4, '05 Comments [12] Posted in ASP.NET | Learning .NET | XmlSerializer
Sponsored By

There' s a pretty nasty XmlFragmentWriter example up on GDN that uses reflection to mess with the internal state of an XmlTextWriter in order to omit the XML declaration. Yikes.

This is an alternate (better) XmlFragmentWriter that's breaks fewer Commandments. It takes code from Sairama, one of our platform engineers at Corillian, to omit the XmlDecl. I took Sai's stuff and added Kzu's xsi/xsd trick to create XML fragments. Here's XmlFragmentWriter.

Given a class (just an example, don't serialize passwords!):

public class AuthenticationInfo

{

    public string Username;

    public string Password;

}

Here's the code and an instance serialized using the standard XmlSerializer. Note the Xml Declaration and the XML schema and instance namespace:

<?xml version="1.0"?>
<AuthenticationInfo
      xmlns:xsd="
http://www.w3.org/2001/XMLSchema
      xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance">
  <Username>user1</Username>
  <Password>pass1</Password>
</AuthenticationInfo>

Standard XmlSerializer fare: 

AuthenticationInfo a = new AuthenticationInfo();

a.Username = "user1";

a.Password = "pass1";

 

XmlSerializer x = new XmlSerializer( typeof(AuthenticationInfo));

XmlTextWriter w2 = new XmlTextWriter(@"c:\bar.xml",null);

w2.Formatting = Formatting.Indented;

x.Serialize( w2, a );

w2.Close();

Here's the same object serialized using our XmlFragmentWriter: 

<AuthenticationInfo>
  <Username>user1</Username>
  <Password>pass1</Password>
</AuthenticationInfo>

And here's how it's used:

AuthenticationInfo a = new AuthenticationInfo();

a.Username = "user1";

a.Password = "pass1";

 

XmlSerializer f = new XmlSerializer( typeof(AuthenticationInfo));

XmlFragmentWriter w = new XmlFragmentWriter(@"c:\foo.xml",null);

w.Formatting = Formatting.Indented;

f.Serialize( w, a );

w.Close();

And here's the XmlFragmentWriter class:

class XmlFragmentWriter : XmlTextWriter

{

    public XmlFragmentWriter(TextWriter w) : base(w){}

    public XmlFragmentWriter(Stream w, Encoding encoding) : base(w, encoding) {}

    public XmlFragmentWriter(string filename, Encoding encoding) :
        base(new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.None), encoding){}

 

    bool _skip = false;

 

    public override void WriteStartAttribute( string prefix, string localName, string ns )

    {

        // STEP 1 - Omits XSD and XSI declarations.

        // From Kzu - http://weblogs.asp.net/cazzu/archive/2004/01/23/62141.aspx

        if ( prefix == "xmlns" && ( localName == "xsd" || localName == "xsi" ) )  

        {

            _skip = true;

            return;

        }

        base.WriteStartAttribute( prefix, localName, ns );

    }

 

    public override void WriteString( string text )

    {

        if ( _skip ) return;

        base.WriteString( text );

    }

 

    public override void WriteEndAttribute()

    {

        if ( _skip )

        {

            // Reset the flag, so we keep writing.

            _skip = false;

            return;

        }

        base.WriteEndAttribute();

    }

 

    public override void WriteStartDocument()

    {

       // STEP 2: Do nothing so we omit the xml declaration.

    }

}

Thanks Kzu and Sairama for giving me these pieces to assemble. I tell you, System.Xml is slick slick slick. Updated with a cleaner solution.

(You can also get rid of the namespaces with another trick but it smells hacky and I don't know what the side effects would be if your document had other namespaces)

Now playing: Akon - Show Out

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. I am a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Friday, August 05, 2005 8:57:24 AM UTC
its just freaky howe we manage to stumble into exactly same things with few hours difference. first ruby unit testing now this.... lightweight solution:

XmlSerializerNamespaces _ns=new XmlSerializerNamespaces(); // can be static private
_ns.Add( "", "" );
_xsMyType.Serialize(writer,obj,_ns);

same effect on output.
Max S
Friday, August 05, 2005 1:24:13 PM UTC
Getting rid of the xml declaration and xsi / xsd namespaces is so hot right now! I just wrote almost the exact same thing yesterday. Creepy.
Friday, August 05, 2005 2:45:20 PM UTC
I needed this in VB.NET, so I've ported it. You can find the port at http://www.blowery.org/code/XmlFragmentWriter-VB.NET.zip

Thanks!
Friday, August 05, 2005 4:05:16 PM UTC
Since you're on the topic - is this approach a bad idea?

public class SampleFragmentXmlTextWriter: XmlTextWriter
{
public SampleFragmentXmlTextWriter( TextWriter w ) : base( w ) {}
public SampleFragmentXmlTextWriter( Stream w, Encoding encoding ) : base( w, encoding ) {}
public SampleFragmentXmlTextWriter( string filename, Encoding encoding ) : base( filename, encoding ) {}

public override void WriteStartDocument()
{
// do nothing so we don't end up with an xml declaration
}
}
buk
Friday, August 05, 2005 4:21:59 PM UTC
Max S. - That's the "hacky" solution that I linked to in this post.
Friday, August 05, 2005 4:38:14 PM UTC
Buk - I like that also! That's even cleaner!
Friday, August 05, 2005 8:51:45 PM UTC
Which is cleaner that the hack I threw together to shift a utf-16 dataset schema into a utf-8 schema that xsd.exe could turn into a nice dataset class for me..

XmlDocument doc = db.ExtractSchemaForStoredProcedure(textBox1.Text,textBox3.Text,textBox2.Text);
XmlDocument newdoc = new XmlDocument();
newdoc.LoadXml(doc.DocumentElement.OuterXml);
MessageBox.Show(newdoc.OuterXml);
create a new declaration so we can switch to utf-8 (xsd.exe likes this)
XmlDeclaration decl= newdoc.CreateXmlDeclaration("1.0","UTF-8",null);
XmlElement root = newdoc.DocumentElement;
newdoc.InsertBefore(decl,root);
XmlTextWriter write = new XmlTextWriter(textBox4.Text,System.Text.Encoding.UTF8);
newdoc.WriteTo(write);
write.Flush();
write.Close();
Saturday, August 06, 2005 8:40:29 AM UTC
Ian - My eyes are literally burning. You've effectively burned my retinas. I hope you're happy.

;)
Sunday, August 07, 2005 5:11:07 AM UTC
yeah, I didn't like it much either! But look on the bright side - I could've posted one of our stored procedures that happen to be written in COBOL.
That's still hurting my brain, but at least we're dogfooding our own DB!

in all seriousness, any idea WHY xsd.exe doesn't like a standard schema spat out by dataSet.getXMLSchema() (ie with UTF-16 rather than UTF-8 )?
It's very annoying. If it were one or two I'd just switch em by hand but it looks like they'll be creating >400 of the things..
Wednesday, August 10, 2005 3:46:34 AM UTC
Scott, honestly, your (and mine therefore!) approach looks more hacky to me than just using the XmlNamespaces class. Besides, it works fine and I never saw a side-effect... Dunno...
Wednesday, August 10, 2005 6:18:13 AM UTC
Another way is to simply create a new document at the beginning of the root element and return with the innerxml.
But this is a cleaner method.
Thanx, Csaba.
Csaba Kabai
Thursday, August 11, 2005 5:43:37 PM UTC
Let me play too! I posted a sample to gotdotnet about a month ago:

http://www.pickabar.com/blog/archives/2005/07/xml_serializati.html


the key parts being:

public override void WriteStartDocument()
{
return;
}

public override void WriteStartDocument(bool standalone)
{
return;
}
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.