1

I am having trouble renaming XML element names. I'm attempting to achieve the following.

Input:

<Addresses>
    <AddressItem>
        <AddressLine1>RZN850PXKYG</AddressLine1>
        <AddressLine2>RYK57IT7Y9Z</AddressLine2>
        <AddressType>Physical</AddressType>
        <City>Some City</City>
        <PostalCode>9999</PostalCode>
        <Province>Some Province</Province>
        <Suburb>Some Suburb</Suburb>
    </AddressItem>
</Addresses>

Expected output:

<b:Addresses>
    <b:AddressItem>
        <b:AddressLine1>RZN850PXKYG</b:AddressLine1>
        <b:AddressLine2>RYK57IT7Y9Z</b:AddressLine1>
        <b:AddressType>Physical</b:AddressType>
        <b:City>Some City</b:City>
        <b:PostalCode>9999</b:PostalCode>
        <b:Province>Some Province</b:Province>
        <b:Suburb>Some Suburb</b:Suburb>
    </b:AddressItem>
</b:Addresses>

Previously I have successfully added attributes and renamed elements using something similar to this:

var xmlDoc = XDocument.Parse(requestObj);

foreach (var element in xmlDoc.Descendants())
{
    if (element.Name.LocalName.StartsWith("something"))
    {
        //example rename element
        element.Name = "request";

        XNamespace b = "http://schemas.something.org/bla/07/WebServices.Stuff";
        XNamespace i = "http://www.w3.org/2001/XMLSchema-instance";

        //set it's attributes
        element.SetAttributeValue(XNamespace.Xmlns + "b", b);
        element.SetAttributeValue(XNamespace.Xmlns + "i", i);
    }
}

I have attempted to achieve my expected result by simply using the below code but the colon is giving me problems it seems:

foreach (var element in xmlDoc.Descendants())
{
    element.Name = "b:" + element.Name;
}

I have searched using the error message received (System.Xml.XmlException: 'The ':' character, hexadecimal value 0x3A, cannot be included in a name.') but none of the solutions seems applicable to elements needing to be renamed recursively.

dbc
  • 104,963
  • 20
  • 228
  • 340
BernardV
  • 640
  • 10
  • 28
  • Did you try and google your error message? I just copied your error and pasted it in Google. This was the first thing that came up: https://stackoverflow.com/questions/2575546/the-character-hexadecimal-value-0x3a-cannot-be-included-in-a-name – Casey Crookston Sep 30 '19 at 18:14
  • You cannot have an element `` without a namespace declaration corresponding to `b`, such as `xmlns:b="https://whatever-you-want-here"`. Without a namespace declaration your XML is not well formed. What namespace do you want? Is it `"http://schemas.something.org/bla/07/WebServices.Stuff"`? – dbc Sep 30 '19 at 19:07
  • @dbc yes I'm adding the name space deceleration to the xml in a seperate process at the moment and then attempting to change the elements manually. – BernardV Oct 01 '19 at 04:00

1 Answers1

1

Assuming that <Addresses> is some nested element in your XML document, and some upper-level node shall declare a namespace xmlns:b="http://schemas.something.org/bla/07/WebServices.Stuff", you can modify your XML as follows:

XNamespace newNamespace = "http://schemas.something.org/bla/07/WebServices.Stuff";

// Move Addresses elements and children into the new namespace
foreach (var e in xmlDoc.Descendants().Where(a => a.Name.LocalName == "Addresses").SelectMany(a => a.DescendantsAndSelf()))
{
    e.Name = newNamespace + e.Name.LocalName;
}

// Optionally assign the namespace prefix "b" to some container element in scope.
xmlDoc.Root.SetAttributeValue(XNamespace.Xmlns + "b", newNamespace.NamespaceName);

Notes:

  1. You are trying to modify the Name property of selected XElement objects to be "b:"+element.Name;, however XElement.Name is not a string, it is an XName, which encapsulates an XML namespace and local name -- not an XML namespace prefix and local name. Thus attempting to assign Name to have a specific prefix fails, and throws an exception.

    In LINQ-to-XML, XElement objects do not know their namespace prefix. They know their actual namespace. Only while outputting an XElement to an XML string will XmlWriter attempt to determine a namespace prefix by looking for an xmlns declaration in scope -- or if none is in scope then it will emit an appropriate xmlns declaration automatically on the current element.

    Thus, to modify an element to look like <b:Addresses>, you must modify its actual namespace to be whatever b: should correspond to. Now, you may not care about the precise prefix chosen, but if you do, you can subsequently specify one on some appropriate element in scope, for instance the root element.

    Demo fiddle #1 here.

  2. If you don't care about the specific prefix used, you can entirely omit the // Optionally code and .Net's XmlWriter will add namespace declarations for you automatically.

    Demo fiddle #2 here.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • thank you very much for you in depth explanation and examples, I will be testing all of this now and mark this as the correct answer if I have no further questions. – BernardV Oct 01 '19 at 06:30