您的位置:首页 > 其它

XmlWriter and Schema

2009-09-02 11:09 274 查看



XmlWriter and Schema

by Rick Strahl
via Rick Strahl's Web Log
on 8/9/2007 8:16:36 AM

In the ResX exporter for my data driven Resource Provider
I use a bit of code that iterates over the database resources and then
spits out ResX resources from the data as an option to get your
resources into your Web site. The code I've used in this stretch of
code uses an XmlWriter to quickly spit out the data.

But there
are a couple of non-critical problems in that code. Several people have
pointed out that the XML generated doesn't exactly match the ResX
format that Visual Studio uses (although as far as I can tell there's
no problem).

The problem is related to an xml:space="preserve"
attribute that Visual Studio sticks onto each resource. It does this so
leading and trailing spaces are preserved when the values are read.
Visual Studio generates:

<
data

name
=
"Today
"
xml:space
=
"preserve
">

<
value
>
Heute </
value
>

</
data
>

but my code generates:

<
data

name
=
"Today
"
d2p1:space
=
"preserve
"
xmlns:d2p1
=
"xml
">

<
value
>
Heute </
value
>

</
data
>

So where's that coming from?

The code I use to generate this Xml Fragment is pretty simple:

xWriter.WriteStartElement("data"
);

xWriter.WriteAttributeString("name"
, ResourceId);

xWriter.WriteAttributeString("space"
,"xml"
,"preserve"
);

xWriter.WriteElementString("value"
, Value);

xWriter.WriteEndElement(); // data

As you can see the xml:space attribute is in fact written out
directly, but the XmlWriter thinks it knows better and tries to
explicitly define the Xml namespace. For the resource editor this
causes no harm as far as I can tell, but it is kinda ugly.

Now the document does have a schema, but originally had written out
the schema simply as a raw text string. There's a raw header and schema
definition:

public
const
string
ResXDocumentTemplate =

@"<?xml version=""1.0"" encoding=""utf-8""?>

<root>

<xsd:schema id=""root"" xmlns="""" xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns:msdata=""urn:schemas-microsoft-com:xml-msdata"">

<xsd:import namespace=""http://www.w3.org/XML/1998/namespace"" />

<xsd:element name=""root"" msdata:IsDataSet=""true"">

<xsd:complexType>

<xsd:choice maxOccurs=""unbounded"">

<xsd:element name=""metadata"">

<xsd:complexType>

<xsd:sequence>

<xsd:element name=""value"" type=""xsd:string"" minOccurs=""0"" />

</xsd:sequence>

<xsd:attribute name=""name"" use=""required"" type=""xsd:string"" />

<xsd:attribute name=""type"" type=""xsd:string"" />

<xsd:attribute name=""mimetype"" type=""xsd:string"" />

<xsd:attribute ref=""xml:space"" />

</xsd:complexType>

</xsd:element>

<xsd:element name=""assembly"">

<xsd:complexType>

<xsd:attribute name=""alias"" type=""xsd:string"" />

<xsd:attribute name=""name"" type=""xsd:string"" />

</xsd:complexType>

</xsd:element>

<xsd:element name=""data"">

<xsd:complexType>

<xsd:sequence>

<xsd:element name=""value"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""1"" />

<xsd:element name=""comment"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""2"" />

</xsd:sequence>

<xsd:attribute name=""name"" type=""xsd:string"" use=""required"" msdata:Ordinal=""1"" />

<xsd:attribute name=""type"" type=""xsd:string"" msdata:Ordinal=""3"" />

<xsd:attribute name=""mimetype"" type=""xsd:string"" msdata:Ordinal=""4"" />

<xsd:attribute ref=""xml:space"" />

</xsd:complexType>

</xsd:element>

<xsd:element name=""resheader"">

<xsd:complexType>

<xsd:sequence>

<xsd:element name=""value"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""1"" />

</xsd:sequence>

<xsd:attribute name=""name"" type=""xsd:string"" use=""required"" />

</xsd:complexType>

</xsd:element>

</xsd:choice>

</xsd:complexType>

</xsd:element>

</xsd:schema>

<resheader name=""resmimetype"">

<value>text/microsoft-resx</value>

</resheader>

<resheader name=""version"">

<value>2.0</value>

</resheader>

<resheader name=""reader"">

<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>

</resheader>

<resheader name=""writer"">

<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>

</resheader>

"
;

which is simply written into the XmlWriter as a raw string:

XmlTextWriter
Writer = new
XmlTextWriter
(this
.FormatResourceSetPath(ResourceSet, LocalResources) + Loc, Encoding
.UTF8);

Writer.Indentation = 3;

Writer.IndentChar = ' '
;

Writer.Formatting = Formatting
.Indented;

xWriter = Writer as
XmlWriter
;

xWriter.WriteRaw(ResXDocumentTemplate);

I suppose that's a very lazy way of doing things but honestly I
didn't see much value in actually hand-coding the schema into
individual XmlWriter commands.

But the above has a couple of problems. First there's the xml:space
issue. There's also a formatting problem - although I have document
indentation enabled the formatting doesn't actually work properly - the
XmlWriter ends up writing a streaming document despite the settings.
The Xml document needs to be treated as an Xml Fragment rather than a
document because the writer doesn't know the the doc header was written
with WriteRaw. Finally the end document tag needs to be explicitly
written out as raw text.

All of this results in quite an ugly looking, albeit functional document.

So I started experimenting a little bit with writing out this
content more cleanly. I could write out the PI and root tag and then
just inject the scheme with WriteRaw which improved the document
parsing somewhat, but still there were schema issues.

But I think a better way is to do something like this instead - load up the 'template' as a full XmlDocument first:

// *** Load the document schema

XmlDocument
doc = new
XmlDocument
();

doc.LoadXml(ResXDocumentTemplate);

and then write out portions of this XmlDocument into the XmlWriter. So when each ResX file is created there's code like this:

XmlTextWriter
Writer = new
XmlTextWriter
(this
.FormatResourceSetPath(ResourceSet, LocalResources) + Loc, Encoding
.UTF8);

...

xWriter.WriteStartElement("root"
);

// *** Write out the schema

doc.DocumentElement.ChildNodes[0].WriteTo(xWriter);

// *** Write out the leading resheader elements

XmlNodeList
Nodes = doc.DocumentElement.SelectNodes("resheader"
);

foreach
(XmlNode
Node in
Nodes)

{

Node.WriteTo(xWriter);

}

This actually writes out each of the components properly into the
XmlWriter which in fact ends up producing a clean and properly
constructed XmlWriter document.

This solves all the formatting issues, with the exception of the
xml:space issue that started me down this path in the first place. It
still fails even with the schema properly written through the XmlWriter.

One solution and probably the cleanest at that is to modify the schema to automatically default to the preserve setting:

<xsd:element name=""data"">

<xsd:complexType>

<xsd:sequence>

<xsd:element name=""value"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""1"" />

<xsd:element name=""comment"" type=""xsd:string"" minOccurs=""0"" msdata:Ordinal=""2"" />

</xsd:sequence>

<xsd:attribute name=""name"" type=""xsd:string"" use=""required"" msdata:Ordinal=""1"" />

<xsd:attribute name=""type"" type=""xsd:string"" msdata:Ordinal=""3"" />

<xsd:attribute name=""mimetype"" type=""xsd:string"" msdata:Ordinal=""4"" />

              <xsd:attribute ref=""xml:space"" default=""preserve""/>

</xsd:complexType>

and then simply don't generate the tag. I checked this out with
two-way conversion - saving values with spaces and exporting to Resx
checking for the spaces still being in the XML (they are) and then
round tripping the data back into the Resource Provider and double
checking the value to ensure that the spaces have made it (they do).

This effectively solves the problem above, but I still wonder how I
would go about generating xml:space="preserve" explicitly into the
XmlWriter. In other situations modifying the schema generically may
simply not be an option.

I'm not sure how to make the XmlWriter recognize the schema
properly. It does work for the Resx editor and for parsing the Xml file
as input using XmlDocument (ie. it passes validation), but now I am
really wondering how to make the proper Xml definition work.

So, how do I get the XmlWriter generate: xml:space="preserve"? Anybody of you Xml Wizards know how to do this? <s>

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: