【问题标题】:C# Change XML value using XpathC# 使用 Xpath 更改 XML 值
【发布时间】:2018-02-07 08:20:00
【问题描述】:

谁能帮助我?我对 C# 有点陌生,但到了那里。 我被设置了一项任务,即从 XML 文件中检索数据、更改数据并使用 Xpath 将其保存在另一个位置。

我认为我的代码“有点”是正确的,但我正在为特定元素路径的语法而苦苦挣扎。我已经包含了一个 XML 文件的示例,它已经过简化,但元素的布局是相同的。

<?xml version="1.0" encoding="utf-8"?>
<Data SomeData which linked to a wepage.>
    <DeviceId>Sometext</DeviceId>
    <Inputs>
        <a:ImageInput>
            <a:Cal>
                <a:Volt1></Volt1>
                <a:Volt2></Volt2>
            </a:Cal>
            <a:Name>Name1</a:Name>
        </a:ImageInput>
        <a:ImageInput>
            <a:Cal>
                <a:Volt1></Volt1>
                <a:Volt2></Volt2>
            </a:Cal>
            <a:Name>Name2</a:Name>  <!-- Need to change this element -->
        </a:ImageInput>
    </Inputs>
</Data>

如您所见,我需要在第二个 a:ImageInput 元素内更改 a:Name 元素。我一直在寻找答案,但我只找到了 Xpath 更改了根元素的一部分的示例。

这是我在 Main() 方法中使用的代码。

public static void ChangeFileXPath()//Changes the value of the node using Xpath
    {
        XmlDocument xmld = new XmlDocument();
        xmld.Load(@"C:\ProgramData\Oxford Instruments NanoAnalysis\Calibration\mics_simulator.xml");

        XmlElement name2 = (XmlElement)xmld.SelectSingleNode("/MicsModule/Inputs/a:ImageInput[@a:Name='BSE']");
        if (name2 != null)
        {
            name2.SetAttribute("a:Name", "{{16}}");
        }

        string AmmendedFile = @"C:\ProgramData\Oxford Instruments NanoAnalysis\XXXX NewXMLReader\Xpath_Mics_Sim.xml";
        xmld.Save(AmmendedFile);
    }

任何帮助将不胜感激。谢谢你的关注。

【问题讨论】:

  • PS..如果这个问题看起来很垃圾,我很抱歉 StackOverflow。我是新手!!
  • 请编辑问题以删除代码图像并替换为文本(格式化为代码)。
  • 那个 xml 似乎缺少 a 前缀的命名空间定义。请提供有效的 XML。您是绑定到 XmlDocument 还是也可以使用 XDocument 代替?这会让你的生活更轻松。
  • @rene 我绑定到 XmlDocument,我无法提供原始 xml 文档,因为它有很多公司资料,我不想让网络看到它。我提供的 xml 图片是完全相同的布局,但我在标签之间遗漏了一些公司信息。 xml 缺少什么命名空间定义?我不确定。
  • @D.Bodd85 SomeData which linked to a wepage 可能是a 的命名空间定义。

标签: c# xml xpath visual-studio-2017


【解决方案1】:
/Data/Inputs/a:ImageInput[@a:Name='Name2']

这是寻找具有 属性 a:Name 和特定值的 a:ImageInput 元素。

但是 XML 有一个 元素 a:Name

要么作为一个元素,用 XPath 处理

/Data/Inputs/a:ImageInput/a:Name[text()='Name2']

然后在代码中设置找到的元素的值。或者切换 XML 以使用属性。

更新查看了 XML

首先:XML 无效。例如。 &lt;a:Volt2&gt;&lt;/Volt2&gt;: 结束名字需要和开头匹配。

第二:虽然技术上 XML 可以在没有命名空间的情况下使用(它们是原始标准的可选扩展),但 .NET 中的 XML 支持将“a:”视为需要命名空间声明(这适用于 XmlReader,即所有不同 XML API 的底层解析器:XmlDocumentXPathDocumentXDocument)。

因此,您需要在文档中添加 XML 命名空间声明(并更正上述不匹配的结束元素):

<?xml version='1.0' encoding='utf-8'?>
<Data xmlns:a='this should be a uri'>
    <DeviceId>Sometext</DeviceId>
    <Inputs>
        <a:ImageInput>
            <a:Cal>
                <a:Volt1></a:Volt1>
                <a:Volt2></a:Volt2>
            </a:Cal>
            <a:Name>Name1</a:Name>
        </a:ImageInput>
        <a:ImageInput>
            <a:Cal>
                <a:Volt1></a:Volt1>
                <a:Volt2></a:Volt2>
            </a:Cal>
            <a:Name>Name2</a:Name>  <!-- Need to change this element -->
        </a:ImageInput>
    </Inputs>
</Data>

鉴于此,XML 将被加载,并且可以被操作。通过对上述 XPath 的更正以及与 XML 的匹配,以下工作:

使用系统; 使用 System.Xml;

class Program
{
    const string Content = @"<?xml version='1.0' encoding='utf-8'?>
<Data xmlns:a='whatever'>
    <DeviceId>Sometext</DeviceId>
    <Inputs>
        <a:ImageInput>
            <a:Cal>
                <a:Volt1></a:Volt1>
                <a:Volt2></a:Volt2>
            </a:Cal>
            <a:Name>Name1</a:Name>
        </a:ImageInput>
        <a:ImageInput>
            <a:Cal>
                <a:Volt1></a:Volt1>
                <a:Volt2></a:Volt2>
            </a:Cal>
            <a:Name>Name2</a:Name>  <!-- Need to change this element -->
        </a:ImageInput>
    </Inputs>
</Data>";
    static void Main(string[] args)
    {
        var xml = new XmlDocument();
        xml.LoadXml(Content);

        XmlNamespaceManager mgr = new XmlNamespaceManager(xml.NameTable);
        mgr.AddNamespace("a", "whatever");
        XmlElement name2 = (XmlElement)xml.SelectSingleNode("/Data/Inputs/a:ImageInput/a:Name[text()='Name2']", mgr);
        if (name2 != null)
        {
            name2.InnerText = "A new value";
        }

        Console.WriteLine(xml.InnerXml);
    }
}

【讨论】:

  • 谢谢你的例子。这确实是有道理的,因为你是完全正确的。我已经尝试了代码,但出现了错误.. System.Xml.XPath.XPathException: '需要命名空间管理器或 XsltContext。此查询具有前缀、变量或用户定义的函数。'
  • @D.Bodd85 现在更新了修改元素的代码。
  • 非常感谢理查德,我必须在数据元素中包含一个命名空间,有一个网络链接,所以我必须先调用它,因为所有其他元素都是其中的一部分。我使用了你的例子,只是添加了路径的额外部分。非常感谢。
  • 100mb xml 文件的方法是否相同?我需要更改节点值,这些节点由 xpath 表达式标识
  • @l--''''''---------'''''''''''' 这是一个新问题,但任何事情都会发生100MB xml 文件的速度很慢,除非您可以避免解析整个内容(忽略毫位作为错字)。
【解决方案2】:

如果您想将 SelectSingleNode 与该 XPath 一起使用,您必须通过提供命名空间管理器来包含命名空间。而且你需要有一个有效的 XML 文件,我在这里根据屏幕截图中的位和你的 XPath 重建了该文件

创建一个 MCVE,此代码将文本值 BSE 的节点更改为 {{16}}。请注意,我在这里假设您的 XML 没有名称空间和前缀。相反,我通过 XmlNamespaceManager 提供它们

// text xml
var xml=@"<Data >
    <Inputs>
    <a:ImageInput>
        <a:Name>BSE</a:Name>
    </a:ImageInput>
    </Inputs>
     </Data>";

    XmlDocument xmld = new XmlDocument();

    XmlNamespaceManager nsmgr; // this will hold our namespaces with their prefixes
    xmld.Load(GetReader(xml, out nsmgr)); // xml can also be your file

    // we are going to find a text node, hence cast to XmlText
    var name2 = (XmlText)xmld.SelectSingleNode(
        "/Data/Inputs/a:ImageInput/a:Name[.='BSE']/text()", 
        nsmgr); // here is the namespace manager so it knows what a is
    if (name2 != null)
    {
        //name2.SetAttribute("a:Name", "{{16}}");
        name2.Value = "{{16}}";
    }

    string AmmendedFile = @"C:\ProgramData\Oxford Instruments NanoAnalysis\XXXX NewXMLReader\Xpath_Mics_Sim.xml";
    xmld.Save(AmmendedFile);
}

// Creates a reader and outputs a namespacemanager that fits for the missing namespace prefixes
XmlReader GetReader(string xml, out XmlNamespaceManager nsmgr)
{
    // code example taken from
    // https://blogs.msdn.microsoft.com/runeetv/2009/02/12/undeclared-namespace-in-xml-eg-xsi-is-an-undeclared-namespace/
    // from author runeetv and https://blogs.msdn.microsoft.com/runeetv/author/runeetvashisht/

    NameTable nt = new NameTable();
    // add missing namespace prefixes
    nsmgr = new XmlNamespaceManager(nt);
    nsmgr.AddNamespace("a", "urn:why-was-this-left-out?");

    XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
    XmlReaderSettings xset = new XmlReaderSettings();
    xset.ConformanceLevel = ConformanceLevel.Fragment;

    // return XmlReader.Create(xml, xset, context); // use this if you want xml to be a filepath
    return XmlReader.Create(new  StringReader(xml), xset, context);
}

【讨论】:

  • 谢谢 rene,这肯定会对未来有所帮助,因为我还有更多此类事情要做。
猜你喜欢
  • 2017-05-18
  • 1970-01-01
  • 2015-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多