首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何将命名空间添加到现有的xml文件

问如何将命名空间添加到现有的xml文件
EN

Stack Overflow用户
提问于 2016-04-04 08:08:02
回答 2查看 695关注 0票数 3

我想打开这个文件并获取以us-gaap开头的所有元素。

代码语言:javascript
复制
ftp://ftp.sec.gov/edgar/data/916789/0001558370-15-001143.txt

为了得到我尝试过的元素:

代码语言:javascript
复制
str = '<html><body><us-gaap:foo>foo</us-gaap:foo></body></html>'
doc = Nokogiri::XML(File.read(str))
doc.xpath('//us-gaap:*')
Nokogiri::XML::XPath::SyntaxError: Undefined namespace prefix: //us-gaap:*
from /Users/ironsand/.rbenv/versions/2.2.2/lib/ruby/gems/2.2.0/gems/nokogiri-1.6.7.2/lib/nokogiri/xml/searchable.rb:165:in `evaluate'

doc.namespaces返回{},因此我认为必须添加名称空间us-gaap。

有一些关于“用Nokogiri添加名称空间”的问题,但看起来是关于如何创建新的XML文档,而不是如何向现有文档添加名称空间。

如何将命名空间添加到现有文档中?

我知道我可以通过Nokogiri::XML::Document#remove_namespaces!删除名称空间,但我不想使用它,因为它也删除了必要的信息。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-04-20 17:39:50

你已经问过XY Problem了。您认为问题在于需要添加一个缺失的命名空间;真正的问题是您要解析的文件是无效的XML。

代码语言:javascript
复制
require 'nokogiri'
doc = Nokogiri.XML( IO.read('0001558370-15-001143.txt') )
doc.errors.length
#=> 5716

例如,在第3行打开的<ACCEPTANCE-DATETIME>‘元素’从未关闭过,在第16行文本中有一个原始符号:

STANDARD INDUSTRIAL CLASSIFICATION: ELECTRIC HOUSEWARES & FANS [3634]

应该作为一个整体逃脱。

但是,文档中有有效的XML片段!特别是,有一个定义xmlns:us-gaap命名空间的XML文档,来自第27243-49312行。让我们只使用根元素定义我们想要的命名空间的知识,以及假设文档中没有任何名称相同的元素,并且根元素在任何属性中都没有一个未转义的>字符。(这些假设对此文件有效,但可能并不适用于每个XML文件。)

代码语言:javascript
复制
txt = IO.read('0001558370-15-001143.txt')
gaap_finder = %r{(<(\w+) [^>]+xmlns:us-gaap=.+?</\2>)}m
txt.scan(gaap_finder) do |xml,_|
  doc = Nokogiri.XML( xml )
  gaaps = doc.xpath('//us-gaap:*')
  p gaaps.length
  #=> 569
end

上面的代码处理txt文件中可能有多个XML文档的情况,尽管在本例中只有一个XML文档。

解码后,gaap_finder regex说:

  • %r{...}m -这是一个正则表达式(允许其中的斜杠,未转义)具有“多行模式”,其中句点将匹配换行符。
  • (...) -捕捉我们发现的一切
  • < -以文字“小于”符号开头
  • (\w+) -查找一个或多个单词字符(标签名),并保存它们
  • -单词字符后面必须有空格(对于避免捕获该文件中的<xsd:xbrl ...>元素非常重要)
  • [^>]+ -后面跟着一个或多个字符,这些字符不是“大于”的符号(以确保我们保持在我们开始使用的相同元素中)。
  • xmlns:us-gaap\s*= -后面跟着这个文字名称空间声明(它可能有空格将它与等号分隔开来)
  • .+? -后面跟着任何东西(尽可能少).
  • </\2> - ...up,直到您看到与我们捕获的起始标记的名称相同的结束标记。

由于regex有捕获组时scan的工作方式,每个结果都是一个双元素数组,其中第一个元素是整个捕获的XML,第二个元素是我们捕获的标记的名称(我们将它分配给_变量“丢弃”)。

如果希望减少捕获的魔力,则文本文件格式似乎总是在<XBRL>...</XBRL>中包装每个XML文档。因此,您可以这样做来处理每个XML文件(有7个,其中5个没有任何us-gaap名称空间):

代码语言:javascript
复制
txt   = IO.read('0001558370-15-001143.txt')
xbrls = %r{(?<=<XBRL>).+?(?=</XBRL>)}m      # find text inside <XBRL>…</XBRL>
txt.scan(xbrls) do |xml|
  doc = Nokogiri.XML( xml )
  if doc.namespaces["xmlns:us-gaap"]
    gaaps = doc.xpath('//us-gaap:*')
    p gaaps.length
  end
end
#=> 569
#=> 0        (for the XML Schema document that defines the namespace)
票数 3
EN

Stack Overflow用户

发布于 2016-04-18 19:00:59

我不知道如何用新的命名空间更新现有的文档,但由于Nokogiri将识别根元素上的命名空间,而这些名称空间在语法上只是属性,所以可以使用新的命名空间声明更新文档,将文档序列化为字符串,然后重新解析:

代码语言:javascript
复制
str = '<html><body><us-gaap:foo>foo</us-gaap:foo></body></html>'
doc_without_ns = Nokogiri::XML(str)
doc_without_ns.root['xmlns:us-gaap'] = 'http://your/actual/ns/here'
doc = Nokogiri::XML(doc_without_ns.to_xml)
doc.xpath("//us-gaap:*")
# Returns [#<Nokogiri::XML::Element:0x3ff375583f9c name="foo" namespace=#<Nokogiri::XML::Namespace:0x3ff375583f24 prefix="us-gaap" href="http://your/actual/ns/here"> children=[#<Nokogiri::XML::Text:0x3ff375583768 "foo">]>]
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36397437

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档