XmlDocument 将 xpath 属性值中的科学记数法解析为数字

XmlDocument Parse scientific notation in xpath attribute value to a number

提问人:Helen Araya 提问时间:6/29/2023 最后编辑:Helen Araya 更新时间:6/29/2023 访问量:72

问:

我有XML,其中的值以科学记数法保存,我需要对该值进行一些比较。但是科学记数法没有按预期工作。

以下是我的代码:

XmlDocument doc = new XmlDocument();
doc.LoadXml("<MainLevel><SubLevel Name=\"test\"  Amount=\"0.0E0\"></SubLevel></MainLevel>");
    
XmlNodeList? xmlLevelsList1 = doc.SelectNodes("MainLevel/SubLevel[@Amount<=275.00]");// this does not return anything because 0.0E0 is in scientific notation                
doc.LoadXml("<MainLevel><SubLevel Name=\"test\"  Amount=\"0.00\"></SubLevel></MainLevel>");

XmlNodeList? xmlLevelsList2 = doc.SelectNodes("MainLevel/SubLevel[@Amount<=275.00]"); this does not return anything because 0.00 is a good format

我想返回 1 个节点而不是 0 个节点。它返回 0,因为xmlLevelsList1

“0.0E0”不是有效数字。

xml xpath xml 解析 xquery

评论

0赞 zx485 6/29/2023
也许这个 SO 问题会对您有所帮助。简而言之:“XSLT 1.0 不支持科学记数法。
0赞 Helen Araya 6/29/2023
@zx485数字(@Amount)没有给我想要的结果。您知道将属性转换为数字的语法如何吗?
0赞 zx485 6/29/2023
是的,也不是。您必须使用 XSLT-2.0。如上所述,XSLT-1.0 不支持科学记数法。该函数仅适用于具有指数数的 XSLT-2.0。上面提到的问题/答案描述了 XSLT-1.0 的解决方法。fn:number(...)
0赞 Helen Araya 6/29/2023
@zx485 我正在使用 2.0,但不确定在我的情况下如何做到这一点。
0赞 zx485 6/29/2023
我用 XSLT-2.0 测试了你的表达式,它们适用于科学数字!您使用支持 XPath-2.0 的 xquery 标记了您的问题,但您的代码看起来像仅支持 XPath-1.0 的 c#。哪个变体是真的?

答:

0赞 LMC 6/29/2023 #1

number() 函数可以在 XPath 1.0 中使用

/MainLevel/SubLevel[number(@Amount) <= 275.00]

鉴于

<?xml version="1.0" encoding="utf-8" ?>
<MainLevel>
    <SubLevel Name="test"  Amount="0.0E0"></SubLevel>
    <SubLevel Name="test1"  Amount="10.0E1"></SubLevel>
    <SubLevel Name="test1"  Amount="4.0E5"></SubLevel>
    <SubLevel Name="test1"  Amount="2.75E2"></SubLevel>
    <SubLevel Name="test1"  Amount="2.76E2"></SubLevel>
</MainLevel>

会回来

<SubLevel Name="test" Amount="0.0E0"/>
<SubLevel Name="test1" Amount="10.0E1"/>
<SubLevel Name="test1" Amount="2.75E2"/>

经测试,用途广泛xmllintlibxml2

 xmllint --xpath '/MainLevel/SubLevel[number(@Amount) <= 275.00]' tmp.xml
<SubLevel Name="test" Amount="0.0E0"/>
<SubLevel Name="test1" Amount="10.0E1"/>
<SubLevel Name="test1" Amount="2.75E2"/>

使用 python 进行测试lxml

from lxml import etree
tree = etree.parse("tmp.xml")
[e.get('Amount') for e in tree.xpath('/MainLevel/SubLevel[number(@Amount) <= 275.00]')]
['0.0E0', '10.0E1', '2.75E2']

评论

0赞 Helen Araya 6/29/2023
由于某种原因,这对我不起作用?我正在使用一个简单的控制台 C# 应用程序。
0赞 Yitzhak Khabinsky 6/29/2023
我在 c# 中尝试了这种方法。它不起作用。😒
0赞 LMC 6/29/2023
我相信这取决于 XPath 的实现。我用那个实现测试了它。xmllintlibxml2
0赞 Yitzhak Khabinsky 6/29/2023
.Net Framework 位于 XML 技术栈 v.1.0(大约 1999-2001 年)上,包括:XPath、XSLT 和 XSD。不支持 XQuery,仅在 MS SQL Server 中。
0赞 LMC 6/29/2023
找不到任何可用功能和 .NET 内容的文档。您可以尝试这个简单的XPath,看看它是如何工作的。它应该返回number("10.0E2")1000
1赞 Yitzhak Khabinsky 6/29/2023 #2

最好使用 LINQ to XML API。自 2007 年以来,它在 .Net Framework 中可用。

正如@zx485已经指出的那样:“XSLT 1.0 不支持科学记数法。

因此,我们不能使用 XPath 表达式。

在 .Net 端转换为双精度数据类型即可完成这项工作。

c#

void Main()
{
    XDocument doc = XDocument.Parse(@"<MainLevel>
            <SubLevel Name='test' Amount='0.0E0'></SubLevel>
            <SubLevel Name='test1'  Amount='10.0E1'></SubLevel>
            <SubLevel Name='test1'  Amount='4.0E5'></SubLevel>
            <SubLevel Name='test1'  Amount='2.75E2'></SubLevel>
            <SubLevel Name='test1'  Amount='2.76E2'></SubLevel>
        </MainLevel>");
    
    var xmlLevelsList1 = doc.Descendants("SubLevel")
        .Where(d => double.Parse(d.Attribute("Amount").Value) <= 275.00);
    Console.WriteLine(xmlLevelsList1);
}

输出

<SubLevel Name="test" Amount="0.0E0"></SubLevel>
<SubLevel Name="test1" Amount="10.0E1"></SubLevel>
<SubLevel Name="test1" Amount="2.75E2"></SubLevel>

评论

0赞 Helen Araya 6/29/2023
我的是 XMLDocument,我没有使用 Linq。有XmlDocument的解决方案吗?
0赞 Michael Kay 6/29/2023
很简单,只需使用 XPath 2.0+ 处理器,例如 Saxon。
1赞 Yitzhak Khabinsky 6/29/2023
@HelenAraya,XMLDocument 它是一种古老的数据类型,大约在 2002 - 2006 年。从 2007 年开始,LINQ to XML 在 .Net Framework 中“取代”了它。
0赞 Martin Honnen 6/29/2023 #3

使用 Saxon.NET:

using System.Xml;
using Saxon.Api;

XmlDocument doc = new XmlDocument();
doc.LoadXml("<MainLevel><SubLevel Name=\"test\"  Amount=\"0.0E0\"></SubLevel></MainLevel>");
    
XmlNodeList? xmlLevelsList1 = doc.SelectNodes("MainLevel/SubLevel[@Amount<=275.00]");// this does not return anything because 0.0E0 is in scientific notation                

Console.WriteLine(xmlLevelsList1.Count);

Processor processor = new Processor();

var docBuilder = processor.NewDocumentBuilder();

var xdmDoc = docBuilder.Wrap(doc);

var xpathCompiler = processor.NewXPathCompiler();

var xdmResult = xpathCompiler.Evaluate("MainLevel/SubLevel[@Amount<=275.00]", xdmDoc);

Console.WriteLine(xdmResult.Count);

输出:

0
1

请注意,虽然您可以针对 Microsoft DOM(即 XmlDocument/XmlNode)运行 XPath 3.1,但 Saxon 有自己的树模型,建议仅在您有无法切换到 Saxon 的本机树模型的旧代码时才使用 Microsoft DOM。