XmlFragmentWriter - Omiting the Xml Declaration and the XSD and XSI namespaces
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)
About Scott
Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.
About Newsletter
Thanks!
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
}
}
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();
;)
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..
But this is a cleaner method.
Thanx, Csaba.
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.
XmlSerializerNamespaces _ns=new XmlSerializerNamespaces(); // can be static private
_ns.Add( "", "" );
_xsMyType.Serialize(writer,obj,_ns);
same effect on output.