如何使用 MSDeployParameters.xml 文件更改命名空间中的 XML 元素?

发布于 2024-11-04 05:26:13 字数 701 浏览 3 评论 0 原文

我无法使用 MSDeploy 更改 Web.config 中的元素。我的 Parameters.xml 文件:

<parameterEntry
  kind="XmlFile"
  scope="\\web.config$"
  match="//spring/objects/object[@id='CultureResolver']/@type" />

Web.config 的相关部分:

<spring>
    <objects xmlns="http://www.springframework.net">

        <object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
             <!--configure for server--> 
            <property name="DefaultCulture" value="en" />
        </object>
    </objects>
</spring>

I can't change an element in Web.config with MSDeploy. My Parameters.xml file:

<parameterEntry
  kind="XmlFile"
  scope="\\web.config$"
  match="//spring/objects/object[@id='CultureResolver']/@type" />

The relevant section of Web.config:

<spring>
    <objects xmlns="http://www.springframework.net">

        <object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
             <!--configure for server--> 
            <property name="DefaultCulture" value="en" />
        </object>
    </objects>
</spring>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

眸中客 2024-11-11 05:26:13

XPath 技巧

你的例子有点棘手(一开始)。但是这个 XPath 查询虽然要长得多,但应该可以工作:

//spring/*[local-name() = 'objects' and namespace-uri() = 'http://www.springframework.net']/*[@id='CultureResolver' and local-name() = 'object' and namespace-uri() = 'http://www.springframework.net']/@type

我在这个答案中发现了这个技巧这个答案 ="https://stackoverflow.com/questions/4861262/declare-namespaces-within-xpath-expression">c# - 在 XPath 表达式中声明命名空间,但我实际上是在之后发现的我已经发布了这个答案(最初)。

我在下面留下了大部分原始答案,并且还添加了有关我原始答案错误的额外部分。

我的示例

我想为 NLog 执行此操作;这是我的 NLog.config 文件的示例:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true">

  ...

  <targets>
    <target name="HipChat" xsi:type="MethodCall" className="DbNecromancer.Logging, DbNecromancer" methodName="SendHipChatRoomNotification">
      <parameter layout="1234567" name="roomId" />
      <parameter layout="blah-blah-blah" name="authToken" />
      <parameter layout="${message-layout}" name="message" />
      <parameter layout="${level}" name="nLogLevel" />
    </target>
  </targets>

  ...
</nlog>

这是我的 Parameters.xml 文件中的相关 XPath 查询,用于更改 layout 属性code>parameter 元素的 name 属性值为“roomId”:

/nlog/targets/target[@name='HipChat']/parameter[@name='roomId']​​/@layout

您可以使用这个免费的在线 XPath 测试器。您还可以使用 Web 部署本身来确认它是否失败!

但我能够在在线测试器和 Web 部署中使其正常工作,而无需使用通配符或删除 NLog.config 中的命名空间声明。诀窍是添加名称空间前缀。

以下是 NLog.config 中修改后的行:

<nlog:nlog xmlns:nlog="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true">
    ...
</nlog:nlog>

这是修改后的 XPath 查询:

/nlog:nlog/targets/target[@name='HipChat']/parameter[@name='roomId']/@layout

您的示例

通过简单地在您的示例使用命名空间的地方添加前缀,我无法让您的示例在在线测试器中运行(而且我没有使用 Spring 的项目)。当我尝试添加前缀时测试器的输出:

错误 - 无法计算 XPath 表达式:前缀必须解析为名称空间:spring

但是,我可以通过执行以下操作(我认为这可能是“正确的”)来使其工作。

以下是我更改的 XML 中的行:

<spring xmlns="" xmlns:spring="http://www.springframework.net">
    <spring:objects>
        ...
    </spring:objects>

这是修改后的 XPath 查询:

/spring/spring:objects/object[@id='CultureResolver']/@type

为什么“我的示例”和“您的示例”是(微妙地)错误的

问题,正如我似乎(几乎)理解的那样,问题在于声明一个没有前缀与使用前缀声明非常不同。

使用前缀声明命名空间只是声明命名空间及其前缀,但不会更改任何元素的命名空间

声明不带前缀的命名空间会使该命名空间成为定义该命名空间的元素以及该元素的所有子元素的默认命名空间。

因此,对于您的示例,您可以修改 XML 中的以下行,并且原始 XPath 查询将起作用:

    <objects xmlns:spring="http://www.springframework.net">

[只需将 spring 前缀添加到命名空间。]

那是因为(或者根据我的工作假设)将前缀添加到命名空间声明而不同时将前缀添加到 objects 元素(及其所有子元素)会“显着”修改 XML,因为它将从命名空间中删除这些元素

要正确添加前缀而不修改 XML 的语义,您的 XML 应如下所示:

<spring>
    <spring:objects xmlns:spring="http://www.springframework.net">
        <spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
             <!--configure for server--> 
            <spring:property name="DefaultCulture" value="en" />
        </spring:object>
    </spring:objects>
</spring>

并且您的 XPath 查询应如下所示:

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

唯一的问题是这不起作用(在 在线测试器,并且可能也不使用 MSDeploy)。

上面的方法不起作用,因为 XPath 引擎需要注册名称空间(与 XML 文档本身中声明的名称空间分开)。但是,正如 Web 部署和在线测试器似乎所做的那样,XML 文档根元素中的任何声明都会自动注册。这非常有用,因为我不知道在使用 Web 部署参数转换时有什么方法来注册命名空间。

有关更深入的解释,请参阅我的相关问题的答案 是否需要在根元素中声明 XML 命名空间才能通过 XPath 查询进行匹配?

您的(更正的)示例

XML:

<spring xmlns:spring="http://www.springframework.net">
    <spring:objects>
        <spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
             <!--configure for server--> 
            <spring:property name="DefaultCulture" value="en" />
        </spring:object>
    </spring:objects>
</spring>

XPath 查询:

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

结论

如果您不介意长 XPath 查询,请使用顶部的技巧。否则,请修改 XML 命名空间声明,以便它们包含前缀并且位于 XML 文档的根元素中。

The XPath Trick

Your example was a little tricky (at first). But this XPath query, while much longer, should work:

//spring/*[local-name() = 'objects' and namespace-uri() = 'http://www.springframework.net']/*[@id='CultureResolver' and local-name() = 'object' and namespace-uri() = 'http://www.springframework.net']/@type

I discovered this trick in this answer to the SO question c# - Declare namespaces within XPath expression, but I actually discovered it after I'd already posted this answer (originally).

I've left most of my original answer below and I also added extra sections about how my original answer is wrong.

My Example

I wanted to do this for NLog; here's an example of my NLog.config file:

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true">

  ...

  <targets>
    <target name="HipChat" xsi:type="MethodCall" className="DbNecromancer.Logging, DbNecromancer" methodName="SendHipChatRoomNotification">
      <parameter layout="1234567" name="roomId" />
      <parameter layout="blah-blah-blah" name="authToken" />
      <parameter layout="${message-layout}" name="message" />
      <parameter layout="${level}" name="nLogLevel" />
    </target>
  </targets>

  ...
</nlog>

Here's the relevant XPath query from my Parameters.xml file to change the layout attribute of the parameter element with a value of "roomId" for the name attribute:

/nlog/targets/target[@name='HipChat']/parameter[@name='roomId']/@layout

You can confirm that the above XPath fails to match the desired attribute using this free online XPath tester. You can also confirm it fails using Web Deploy itself!

But I was able to get this to work, both in the online tester and via Web Deploy, without using wildcards or removing the namespace declaration in NLog.config. The trick was to add a namespace prefix.

Here are the modified lines in NLog.config:

<nlog:nlog xmlns:nlog="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" throwExceptions="true">
    ...
</nlog:nlog>

Here's the modified XPath query:

/nlog:nlog/targets/target[@name='HipChat']/parameter[@name='roomId']/@layout

Your Example

I couldn't get your example to work in the online tester by simply adding a prefix where your example is using a namespace (and I don't have a project that uses Spring). The output of the tester when I try to just add a prefix:

ERROR - Failed to evaluate XPath expression: Prefix must resolve to a namespace: spring

However, I was able to get it to work by doing the following (which I think might be 'correct').

Here's the lines in your XML I changed:

<spring xmlns="" xmlns:spring="http://www.springframework.net">
    <spring:objects>
        ...
    </spring:objects>

Here's the modified XPath query:

/spring/spring:objects/object[@id='CultureResolver']/@type

Why 'My Example' and 'Your Example' are (Subtly) Wrong

The problem, as I seem to (barely) understand it, is that declaring a namespace without a prefix is very different than declaring one with a prefix.

Declaring a namespace with a prefix simply declares the namespace and its prefix, but it doesn't change the namespace of any elements.

Declaring a namespace without a prefix makes that namespace the default namespace for both the element in which its defined and for all of the child elements of that element.

So, for your example, you could modify the following line in your XML and your original XPath query will work:

    <objects xmlns:spring="http://www.springframework.net">

[Just add the spring prefix to the namespace.]

That's because (or is according to my working hypothesis) that adding the prefix to the namespace declaration without also adding the prefix to the objects element (and all its children) is 'significantly' modifying the XML because it's removing those elements from the namespace.

To correctly add a prefix without modifying the semantics of the XML, your XML should be as follows:

<spring>
    <spring:objects xmlns:spring="http://www.springframework.net">
        <spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
             <!--configure for server--> 
            <spring:property name="DefaultCulture" value="en" />
        </spring:object>
    </spring:objects>
</spring>

And your XPath query should be like this:

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

The only problem is that that doesn't work (in the online tester, and probably not with MSDeploy either).

The above doesn't work because XPath engines require namespaces to be registered (separate from the namespaces declared in the XML document itself). But, as both Web Deploy and the online tester seem to do, any declarations in the root element of the XML document are automatically registered. And that's very useful as I don't know of any way to register namespaces when using Web Deploy parameter transformation.

For a more in-depth explanation see the answers to my related question Do XML namespaces need to be declared in the root element to be matchable by an XPath query?.

Your (Corrected) Example

XML:

<spring xmlns:spring="http://www.springframework.net">
    <spring:objects>
        <spring:object id="CultureResolver" type="Spring.Globalization.Resolvers.SessionCultureResolver, Spring.Web">
             <!--configure for server--> 
            <spring:property name="DefaultCulture" value="en" />
        </spring:object>
    </spring:objects>
</spring>

XPath query:

//spring/spring:objects/spring:object[@id='CultureResolver']/@type

Conclusion

If you don't mind long XPath queries, use the trick at the top. Otherwise, modify your XML namespace declarations so that they include a prefix and they're located in the root element of your XML document.

欢烬 2024-11-11 05:26:13

问题在于 元素上的名称空间声明。您的 XPath 查询没有匹配项,因为不存在具有空命名空间的 元素(这是查询正在查找的内容)。

现在,在 XPath 中指定 XML 命名空间是一个棘手的问题(在这种情况下甚至是不可能的),所以我建议您改用这个表达式:

"//spring/*/*[@id='CultureResolver']/@type"

HTH...

the problem is the namespace declaration on the <objects/> element. Your XPath query doesn't have a match because there is no <objects/> element with an empty namespace (which is what the query is looking for).

Now, specifying XML namespaces in XPath is a tricky issue (in this case it's even impossible), so I'd suggest you use this expression instead:

"//spring/*/*[@id='CultureResolver']/@type"

HTH...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文