【问题标题】:How To change XML namespace of certain element如何更改某些元素的 XML 命名空间
【发布时间】:2012-06-21 15:34:04
【问题描述】:

我通过一些 WCF 消息的 xmlserialization 生成了一些 xml。 现在我想创建一个通用方法,在该方法中我将提供一个 xml 文件名和一个前缀,如 mailxml12。 然后在 xml 文件中,那些名称中没有任何命名空间前缀的元素应替换为 mailxml12:

喜欢的源文件是:

<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>

在需要的方法之后,它应该被更改为所有没有mailxml:前缀的元素名称。 喜欢DeliveryApptCreateRequest 应该变成mailxml:DeliveryApptCreateRequest 而像d2p1:CompanyName 这样的元素应该保持原样。

我试过下面的代码

 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);
    }

唯一的问题是根元素保持原样,而所有元素都根据我的需要进行更改

【问题讨论】:

  • 你的意思也只是标签元素或属性?
  • 我已经发布了将您的 xml 解析为字符串的代码,因此尽管名称空间声明等存在问题,但它应该可以工作。但是,您应该尝试确定为什么其他建议的解决方案在您的应用程序。

标签: c# .net xml xml-parsing


【解决方案1】:

您可以将整个 XML 解析为字符串并在适当的位置插入名称空间。但是,这种解决方案可以创建大量仅在算法中使用的新字符串,这对性能不利。但是,我已经编写了一个以这种方式解析它的函数,对于您发布的示例 XML,它似乎运行得非常快;)。如果您想使用它,我可以发布它。

另一种解决方案是将 XML 加载为 XmlDocument 并利用它是树状结构这一事实。这样,您可以创建一个方法,在适当的地方递归地添加适当的命名空间。 不幸的是,XmlNode.Name 属性是只读的,这就是为什么您必须手动复制 xml 的整个结构来更改某些节点的名称的原因。 我现在没时间写代码,就让你写吧。如果您遇到任何问题,请告诉我。

更新

我已经测试了您的代码和 Jeff Mercado 建议的代码,它们似乎都可以正常工作,至少在您在问题中发布的示例 XML 中是这样。确保您尝试解析的 XML 与您发布的 XML 相同。

为了使它工作并解决最初提出的添加命名空间问题,您可以使用代码,它将整个 XML 处理为 String 并手动解析它:

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;
    }

您可以检查此代码以使您的应用正常运行,但是,我强烈建议您确定建议的其他解决方案为何不起作用。

【讨论】:

  • 你的意思是“命名空间”还是“命名空间前缀”?
  • 您的代码明显更短,因此我对其进行了测试,它似乎可以正常工作。它为根元素和其他未定义命名空间的元素添加了前缀。我使用了你问题中可用的 XML 作为测试样本。另外,请查看 Jeff Mercando 的答案,因为在这种情况下可能会令人满意。如果这对您没有帮助,请告诉我,以便我们寻找其他解决方案。
  • Lucas 是否使用我提到的代码更改了根元素?
  • 是的,根元素也已更改。
【解决方案2】:

由于在这种情况下您定义了默认命名空间,您可以删除默认命名空间声明并使用旧命名空间名称为新前缀添加新声明,从而有效地替换它。

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));

【讨论】:

  • 但是没有用。运行并保存文档根元素后,保留为 DeliveryApptCreateRequest 而不是成为 mailxml:DeliveryApptCreateRequest
  • @KamranShahid:我不相信。如果您的 XML 看起来像您向我们展示的那样,那么它应该可以工作,这当然适用于您的示例。 XML 真的 像您在示例中显示的那样吗?
  • 我测试了您的解决方案建议,它适用于问题中发布的 XML 示例。
  • 在这个讨论中,类之间存在混淆。但是“XmlElement”与“XElement”(以及“XmlDocument”与“XDocument”等)是不同名称空间中的不同类。它们的行为不同,具有不同的语法和功能。请不要混淆它们。
【解决方案3】:

@JeffMercado 的解决方案对我不起作用(可能是因为我没有默认命名空间)。

我最终使用了:

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

要更改我使用的整个文档的命名空间:

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);
}

用法:

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

HTH

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多