8

I have some set of xml generated via xmlserialization of some WCF messages. Now I want to make a generic method in which I will provide an xml filename and a prefix like mailxml12. Then in xml file those elements that don't have any namespace prefix in their name should be replaced with mailxml12:

Like source file is:

<DeliveryApptCreateRequest d2p1:ApptType="Pallet" d2p1:PickupOrDelivery="Delivery" d2p1:ShipperApptRequestID="4490660303D5" d2p1:SchedulerCRID="234234" xmlns:d2p1="http://idealliance.org/Specs/mailxml12.0a/mailxml_defs" xmlns="http://idealliance.org/Specs/mailxml12.0a/mailxml_tm">
<SubmittingParty d2p1:MailerID6="123446" d2p1:CRID="342343" d2p1:MaildatUserLicense="A123" />
<SubmittingSoftware d2p1:SoftwareName="asds" d2p1:Vendor="123" d2p1:Version="12" />
<SubmitterTrackingID>2CAD3F71B4405EB16392</SubmitterTrackingID>
<DestinationEntry>No</DestinationEntry>
<OneTimeAppt>
  <PreferredAppt>2012-06-29T09:00:00Z</PreferredAppt>
</OneTimeAppt>    
<TrailerInfo>
  <Trailer>
    <TrailerNumber>A</TrailerNumber>
    <TrailerLength>20ft</TrailerLength>
  </Trailer>
  <Carrier>
    <CarrierName>N/A</CarrierName>
    <URL>http://test.com</URL>
  </Carrier>
  <BillOfLadingNumber>N/A</BillOfLadingNumber>
</TrailerInfo>   
</DeliveryApptCreateRequest>

After the desired method it should be changed into all element name which doesn't have prefix with mailxml:. Like DeliveryApptCreateRequest should become mailxml:DeliveryApptCreateRequest while element like d2p1:CompanyName should remain as it is.

I have tried with following code

 private void RepalceFile(string xmlfile)
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(xmlfile);
        var a = doc.CreateAttribute("xmlns:mailxml12tm");
        a.Value = "http://idealliance.org/Specs/mailxml12.0a/mailxml_tm";
        doc.DocumentElement.Attributes.Append(a);
        doc.DocumentElement.Prefix = "mailxml12tm";

        foreach (XmlNode item in doc.SelectNodes("//*"))
        {
            if (item.Prefix.Length == 0)
                item.Prefix = "mailxml12tm";
        }
        doc.Save(xmlfile);
    }

only problem with it is that root element remain as it is while all are changed as i needed

2
  • You mean only tag elements or attributes as well? Commented Jun 19, 2012 at 18:56
  • I've posted code that parsed your xml as a string, so it should work despite issues with namespace declarations etc. However, you should try to determine why the other suggested solution didn't work in your application. Commented Jun 22, 2012 at 18:24

3 Answers 3

2

You can just parse the whole XML as a string and insert namespaces where appropriate. This solution, however, can create lots of new strings only used within the algorithm, which is not good for the performance. However, I've written a function parsing it in this manner and it seems to run quite fast for sample XML you've posted ;). I can post it if you would like to use it.

Another solution is loading XML as XmlDocument and taking advantage of the fact it's a tree-like structure. This way, you can create a method recursively adding appropriate namespaces where appropriate. Unfortunately, XmlNode.Name attribute is read-only and that's why you have to manually copy the entire structure of the xml to change names of some nodes. I don't have time to write the code right now, so I just let you write it. If you encounter any issues with it, just let me know.

Update

I've tested your code and code suggested by Jeff Mercado and both of them seem to work correctly, at least in the sample XML you've posted in the question. Make sure the XML you are trying to parse is the same as the one you've posted.

Just to make it work and solve adding namespace issue originally asked, you can use the code, which handles the whole XML as a String and parses it manually:

private static String UpdateNodesWithDefaultNamespace(String xml, String defaultNamespace)
    {
        if (!String.IsNullOrEmpty(xml) && !String.IsNullOrEmpty(defaultNamespace))
        {
            int currentIndex = 0;

            while (currentIndex != -1)
            {
                //find index of tag opening character
                int tagOpenIndex = xml.IndexOf('<', currentIndex);

                //no more tag openings are found
                if (tagOpenIndex == -1)
                {
                    break;
                }

                //if it's a closing tag
                if (xml[tagOpenIndex + 1] == '/')
                {
                    currentIndex = tagOpenIndex + 1;
                }
                else
                {
                    currentIndex = tagOpenIndex;
                }

                //find corresponding tag closing character
                int tagCloseIndex = xml.IndexOf('>', tagOpenIndex);
                if (tagCloseIndex <= tagOpenIndex)
                {
                    throw new Exception("Invalid XML file.");
                }

                //look for a colon within currently processed tag
                String currentTagSubstring = xml.Substring(tagOpenIndex, tagCloseIndex - tagOpenIndex);
                int firstSpaceIndex = currentTagSubstring.IndexOf(' ');
                int nameSpaceColonIndex;
                //if space was found
                if (firstSpaceIndex != -1)
                {
                    //look for namespace colon between tag open character and the first space character
                    nameSpaceColonIndex = currentTagSubstring.IndexOf(':', 0, firstSpaceIndex);
                }
                else
                {
                    //look for namespace colon between tag open character and tag close character
                    nameSpaceColonIndex = currentTagSubstring.IndexOf(':');
                }

                //if there is no namespace
                if (nameSpaceColonIndex == -1)
                {
                    //insert namespace after tag opening characters '<' or '</'
                    xml = xml.Insert(currentIndex + 1, String.Format("{0}:", defaultNamespace));
                }

                //look for next tags after current tag closing character
                currentIndex = tagCloseIndex;
            }
        }

        return xml;
    }

You can check this code out in order to make you app working, however, I strongly encourage you to determine why the other solutions suggested didn't work.

Sign up to request clarification or add additional context in comments.

4 Comments

Do you mean "namespace", or "namespace prefix"?
Your code is significantly shorter, so I tested it and it seems to work correctly. It added a prefix to the root element and the other elements without namespace previously defined. I used XML that's available in yor question as a test sample. Also, take a look at an answer from Jeff Mercando as it may be satisfactory in this case. If this doesn't help you, let me know, so we'll look for another solutions.
Lucas has the root element get changed at your end with my mentioned code?
Yes, root element was changed as well.
2

@JeffMercado's solution didn't work for me (probably since I didn't have a default namespace).

I ended up using:

XNamespace ns = Constants.Namespace;
el.Name = (ns + el.Name.LocalName) as XName;

To change the namespace of a whole document I used:

private void rewriteNamespace(XElement el)
{
    // Change namespace
    XNamespace ns = Constants.Namespace;
    el.Name = (ns + el.Name.LocalName) as XName;

    if (!el.HasElements)
        return;

    foreach (XElement d in el.Elements())
        rewriteNamespace(d);
}

Usage:

var doc = XDocument.parse(xmlStr);
rewriteNamespace(doc.Root)

HTH

Comments

1

Since in this case you have a default namespace defined, you could just remove the default namespace declaration and add a new declaration for your new prefix using the old namespace name, effectively replacing it.

var prefix = "mailxml";
var content = XElement.Parse(xmlStr);
var defns = content.GetDefaultNamespace();
content.Attribute("xmlns").Remove();
content.Add(new XAttribute(XNamespace.Xmlns + prefix, defns.NamespaceName));

4 Comments

But it didn't work. After running and saving the doc root element remain as DeliveryApptCreateRequest instead of becoming mailxml:DeliveryApptCreateRequest
@KamranShahid: I don't believe that. If your XML looked how you have shown us, then it should have worked, this certainly works for your example. Is the XML really like what you have shown in your example?
I tested your solution suggestion and it worked for the XML sample posted in the question.
Across this discussion there is a confusion between classes. But "XmlElement" vs. "XElement" (as well as "XmlDocument" vs. "XDocument" and so on) are different classes in different name spaces. And they behave differently and have different syntax and features. Please do not mix them up.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.