1

I am trying to reproduce the following line from an SQL Server .regsrvr file. It does not include the prefix on the element name, 'definitions'.

<definitions xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08">

If I use the three (3) parameter version of WriteStartElement()

$xmlw.WriteStartElement('sfc', 'definitions', 'http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08')

it produces the prefix on the element name.

<sfc:definitions xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08" />

If I use the two (2) parameter version of WriteStartElement()

$xmlw.WriteStartElement('definitions', 'http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08')

it does not include the prefix 'sfc'.

<definitions xmlns="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08" />

Does it matter if the prefix is present on the element name?

PS C:\> $PSVersionTable.PSVersion.ToString()
7.2.7

Update:

@mklement0 - is this correct?

This is from the .regsrvr created by SSMS. The definitions element creates sfc as the "default" namespace for descendent elements. The document, docinfo, and aliases elements are considered part of the sfc namespace.

If the definitions element was given as sfc:definitions, then the descendent elements would be considered as part of the parent xs namespace.

If sfc became the namespace for descendents of definitions, does sfc:version need to have the sfc prefix?

<xs:bufferSchema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <definitions xmlns:sfc="http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08">
    <document>
      <docinfo>
        <aliases>
          <alias>/system/schema/RegisteredServers</alias>
        </aliases>
        <sfc:version DomainVersion="1" />
      </docinfo>
      <data>
lit
  • 14,456
  • 10
  • 65
  • 119
  • I only saw your update now (@-mentioning users in the _body_ of a post never results in a notification). No, the descendants would _not_ be considered part of the parent `xs` namespace, given that that namespace is defined with an _explicit prefix_ - only those descendants that use `` explicitly would be in that namespace. So if you changed `` to ``, the same would apply there: only `` descendants would be in that namespace - you've effectively only changed the namespace for `` itself. – mklement0 Apr 17 '23 at 16:50
  • So it sounds like you were looking for `` after all, which puts `` and all its descendants into that namespace by default. – mklement0 Apr 17 '23 at 16:57

1 Answers1

0

Does it matter if the prefix is present on the element name?

It doesn't matter for the element itself, because in both cases the element is considered to be in the http://schemas.microsoft.com/sqlserver/sfc/serialization/2007/08 namespace, but it has implications for descendant elements:

  • <definitions xmlns="http://..." /> puts not only the element at hand in that namespace, but also makes that namespace the implied default namespace for descendant elements that do no that themselves declare a new namespace.

    • That is, descendant elements that also do not use a namespace prefix and also do not use xmlns='...' (to declare a new default namespace for all descendants) are implicitly considered to be in the same namespace.
  • <sfc:definitions xmlns:sfc="http://..." /> puts the element in that namespace by explicit prefix (sfc:).

    • This means that descendant elements too need to use the same prefix in order to be considered part of the same namespace.

    • Otherwise, elements that do not themselves declare a new (default) namespace are considered to be in any higher-up element's default namespace or, if there is none, in no namespace at all.

The following simplified example demonstrates this:

# Sample input XML
[xml] $xml = @'
<xml>
    <sfc:definitions id="1" xmlns:sfc="http://example.org/def1">
        <sfc:explicit>hi1</sfc:explicit>
    </sfc:definitions>
    <definitions id="2" xmlns="http://example.org/def2">
        <implicit>hi2</implicit>
    </definitions>    
</xml>
'@

# Loop over all <definitions> child elements and report
# their respect child element's effective namespace URI
$xml.xml.definitions | % {
  $_.ChildNodes | % {
    [PSCustomObject]@{
      Child = $_.OuterXml
      Namespace = $_.NamespaceUri
    }
  }
}

Output:

Child                                                                Namespace
-----                                                                ---------
<sfc:explicit xmlns:sfc="http://example.org/def1">hi1</sfc:explicit> http://example.org/def1
<implicit xmlns="http://example.org/def2">hi2</implicit>             http://example.org/def2

However, the first element quoted in your question, from your SQL Server .regsrvr file can not be written as such with a single .WriteStartElement() call, because it combines an un-prefixed name with defining a namespace with prefix, whereas the WriteStartElement() overloads offer only the following variations:

  • create an un-prefixed element name without a namespace attribute (e.g. <definitions>),
  • create an un-prefixed name with a default namespace attribute (e.g. <definitions xmlns="...">); however, if the namespace URI was used in a prefix declaration in an ancestral element, say with prefix foo, you'll get <foo:definitions> instead.
  • create a prefixed element name with a namespace attribute with prefix (e.g. <sfc:definitions xmlns:sfc="...">)

The workaround is to manually create the xmlns:sfc attribute, using WriteAttributeString(); here's a self-contained example:

$sb = [System.Text.StringBuilder]::new()
$writer = [System.XML.XmlWriter]::Create($sb)

$writer.WriteStartElement('definitions')
$writer.WriteAttributeString('xmlns', 'sfc', $null, 'http://example.org')
$writer.WriteEndElement()

$writer.Dispose()

# Output the completed documented as a string.
($xmlText = $sb.ToString())

Result:

<?xml version="1.0" encoding="utf-16"?><definitions xmlns:sfc="http://example.org" />

Note the un-prefixed element name, definitions, combined with the namespace attribute that declares a prefix, xmlns:sfc="...".
This element is therefore itself not in that namespace.


Implications of the presence of namespaces for element access / queries:

  • PowerShell's adaptation of the XML DOM using dot notation, as shown above, ignores namespaces, meaning that only the (prefix-less) element names matter, irrespective of what namespace they're in (whether it be by default or with an explicit prefix).

  • By contrast, using XPath to query [xml] (System.Xml.XmlDocument) instances requires explicit namespace handling:

    • For using XPath queries with the Select-Xml cmdlet, see this answer.

    • For using XPath queries with the [xml] .NET type's .SelectNodes() and .SelectSingleNodes() methods, see this answer.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • thank you for your detailed and very helpful answer. It contains a lot of good information. Is it possible to use `WriteStartElement()` to produce the `definitions` element without the prefix on the element name? – lit Nov 28 '22 at 16:29
  • Glad to hear it, @lit. If you use the _single-parameter_ `WriteStartElement()` overload, you should get an un-prefixed element name (and no `xmlns` attribute); i.e., in your case, `.WriteStartElement('definitions')` – mklement0 Nov 28 '22 at 18:51
  • I have also tried the _single-parameter_ overload, but it does not include any URI. My goal is to reproduce the original .regsrvr file line, so I am not there yet. I have not spent the time needed to see if also having the prefix on the element name (unlike the .regsrvr file) causes any difference when SSMS reads it. – lit Nov 29 '22 at 17:00
  • @lit, good point: it _would_ make a difference, because the original element combines an _unprefixed_ element name with an namespace attribute _with prefix_, which `.WriteStartElement()` cannot directly produce; please see my update for a solution. – mklement0 Nov 29 '22 at 17:55