简体   繁体   中英

How to hoist XML namespaces to root element

If I have an XML file with namespaces like:

<root>

<h:table xmlns:h="http://www.namespaces.com/namespaceOne">
  <h:tr>
    <h:td>Apples</h:td>
    <h:td>Bananas</h:td>
  </h:tr>
</h:table>

<h:table xmlns:h="https://www.namespaces.com/namespaceTwo">
  <h:name>African Coffee Table</h:name>
  <h:width>80</h:width>
  <h:length>120</h:length>
</h:table>

</root>

I want to hoist all of the namespaces to the root element, like this:

<root xmlns:h="http://www.namespaces.com/namespaceOne" xmlns:h1="https://www.namespaces.com/namespaceTwo">

<h:table>
  <h:tr>
    <h:td>Apples</h:td>
    <h:td>Bananas</h:td>
  </h:tr>
</h:table>

<h1:table>
  <h1:name>African Coffee Table</h1:name>
  <h1:width>80</h1:width>
  <h1:length>120</h1:length>
</h1:table>

</root>

Is there a way to do this? Ideally automatically resolving conflicting namespace prefixes, as in the example above. I haven't committed to using Linq to XML or System.Xml yet, so either would be a possibility.

There is one major constraint: because of the environment I am working in, I can't write classes. I can write functions, but no new class definitions.

Turns out this is pretty straightforward:

var doc = XDocument.Parse(xml);
var namespaceAttributes = doc.Descendants()
    .SelectMany(x => x.Attributes())
    .Where(x => x.IsNamespaceDeclaration);
int count = 1;
foreach (var namespaceAttribute in namespaceAttributes)
{
    doc.Root.Add(new XAttribute(XNamespace.Xmlns + $"h{count}", namespaceAttribute.Value));
    namespaceAttribute.Remove();
    count++;
}

We loop through all namespace declarations ( xmlns:foo="foo" ). For each one we find, we put a namespace attribute with the same URL on the root element, and remove that one.

Demo .

Note that this does slightly odd things if you have multiple namespaces with the same URL (eg if you have two lots of xmlns:h="https://www.namespaces.com/namespaceOne" on different children): it puts multiple xmlns declarations on the root element with the same URL, but all elements use the last such namespace. If you want to avoid that, just keep a list of namespaces you've added to the root element.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM