c# - XDocument duplicate namespace with different Local Name -
i have xml document looks this:
<schema namespace="bbsf_model" alias="self" p1:usestrongspatialtypes="false" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns:p1="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm"> <entitytype name="customer"> <property name="id" type="guid" nullable="false" /> </entitytype> </schema>
i used below code modify property element of document:
xelement csdleentity = csdldoc.root.descendants() .where(d => d.name.localname == "entitytype") .firstordefault(e => e.attribute("name").value == "customer"); var csdlproperty = csdleentity.descendants() .where(d => d.name.localname == "property") .firstordefault(e => e.attribute("name").value == "id"); xnamespace annotation = "http://schemas.microsoft.com/ado/2009/02/edm/annotation"; var attrib = new xattribute(annotation + "storegeneratedpattern", "computed"); csdlproperty.add(attrib);
when save xdocument property element looks like:
<property name="id" type="guid" nullable="false" p1:storegeneratedpattern="computed" />
however want is:
<property name="id" type="guid" nullable="false" annotation:storegeneratedpattern="computed" />
the problem xmlns "http://schemas.microsoft.com/ado/2009/02/edm/annotation" referenced twice in root node of document 2 different aliases/localnames (annotation, p1)
xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns:p1="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
i cannot change or tamper root node.
how can save document or update property element give required output?
in theory, shouldn't matter whether use prefix p1:storegeneratedpattern="computed"
or annotation:storegeneratedpattern="computed"
, since these mean exactly same thing -- element expanded xml name of {http://schemas.microsoft.com/ado/2009/02/edm/annotation}storegeneratedpattern
. if receiving xml parser (or qa department?) has problem dealing this, easiest fix may fix parser conform standard.
that being said, reference source xelement
, turns out namespace/prefix attribute pairs pushed onto push-down stack in order of addition while writing, checked matches against element namespace top bottom of stack -- doing match in reverse order in attributes added. if wanted use prefix annotation
, permute order of duplicated namespaces in root element. (for details, see here.)
but, wrote cannot change or tamper root node. thus, you're going forced bit of work: you're going need create own wrapper subclass of xmlwriter
, prefix remapping yourself.
first, non-abstract subclass of xmlwriter
wraps "real" xmlwriter
:
public class xmlwriterproxy : xmlwriter { readonly xmlwriter basewriter; public xmlwriterproxy(xmlwriter basewriter) { if (basewriter == null) throw new argumentnullexception(); this.basewriter = basewriter; } protected virtual bool issuspended { { return false; } } public override void close() { basewriter.close(); } public override void flush() { basewriter.flush(); } public override string lookupprefix(string ns) { return basewriter.lookupprefix(ns); } public override void writebase64(byte[] buffer, int index, int count) { if (issuspended) return; basewriter.writebase64(buffer, index, count); } public override void writecdata(string text) { if (issuspended) return; basewriter.writecdata(text); } public override void writecharentity(char ch) { if (issuspended) return; basewriter.writecharentity(ch); } public override void writechars(char[] buffer, int index, int count) { if (issuspended) return; basewriter.writechars(buffer, index, count); } public override void writecomment(string text) { if (issuspended) return; basewriter.writecomment(text); } public override void writedoctype(string name, string pubid, string sysid, string subset) { if (issuspended) return; basewriter.writedoctype(name, pubid, sysid, subset); } public override void writeendattribute() { if (issuspended) return; basewriter.writeendattribute(); } public override void writeenddocument() { if (issuspended) return; basewriter.writeenddocument(); } public override void writeendelement() { if (issuspended) return; basewriter.writeendelement(); } public override void writeentityref(string name) { if (issuspended) return; basewriter.writeentityref(name); } public override void writefullendelement() { if (issuspended) return; basewriter.writefullendelement(); } public override void writeprocessinginstruction(string name, string text) { if (issuspended) return; basewriter.writeprocessinginstruction(name, text); } public override void writeraw(string data) { if (issuspended) return; basewriter.writeraw(data); } public override void writeraw(char[] buffer, int index, int count) { if (issuspended) return; basewriter.writeraw(buffer, index, count); } public override void writestartattribute(string prefix, string localname, string ns) { if (issuspended) return; basewriter.writestartattribute(prefix, localname, ns); } public override void writestartdocument(bool standalone) { basewriter.writestartdocument(standalone); } public override void writestartdocument() { basewriter.writestartdocument(); } public override void writestartelement(string prefix, string localname, string ns) { if (issuspended) return; basewriter.writestartelement(prefix, localname, ns); } public override writestate writestate { { return basewriter.writestate; } } public override void writestring(string text) { if (issuspended) return; basewriter.writestring(text); } public override void writesurrogatecharentity(char lowchar, char highchar) { if (issuspended) return; basewriter.writesurrogatecharentity(lowchar, highchar); } public override void writewhitespace(string ws) { if (issuspended) return; basewriter.writewhitespace(ws); } }
next, subclass allows attribute namespace prefixes remapped:
public class prefixselectingxmlwriterproxy : xmlwriterproxy { readonly stack<xname> elements = new stack<xname>(); readonly func<string, string, string, stack<xname>, string> attributeprefixmap; public prefixselectingxmlwriterproxy(xmlwriter basewriter, func<string, string, string, stack<xname>, string> attributeprefixmap) : base(basewriter) { if (attributeprefixmap == null) throw new nullreferenceexception(); this.attributeprefixmap = attributeprefixmap; } public override void writestartattribute(string prefix, string localname, string ns) { prefix = attributeprefixmap(prefix, localname, ns, elements); base.writestartattribute(prefix, localname, ns); } public override void writestartelement(string prefix, string localname, string ns) { base.writestartelement(prefix, localname, ns); elements.push(xname.get(localname, ns)); } public override void writeendelement() { base.writeendelement(); elements.pop(); // pop after base.writeendelement() lets base class throw exception on stack error. } }
then use like:
string xml; using (var sw = new stringwriter()) { using (var xmlwriter = xmlwriter.create(sw, new xmlwritersettings { indent = true, indentchars = " " })) using (var xmlwriterproxy = new prefixselectingxmlwriterproxy(xmlwriter, (string prefix, string localname, string ns, stack<xname> parents) => { if (localname == "storegeneratedpattern" && ns == annotation && parents.peek() == xname.get("property", "http://schemas.microsoft.com/ado/2009/11/edm")) return "annotation"; return prefix; }) ) { csdldoc.writeto(xmlwriterproxy); } xml = sw.tostring(); } debug.writeline(xml);
as can see remaps attribute prefixes, extended remap element prefixes overriding writestartelement(string prefix, string localname, string ns
).
working fiddle.
Comments
Post a Comment