我有一个XML文档,我试图通过使用C#解析。在本文档中,我将以下为例:
<text>
<body>
<pb facs="somestring" />
<opener>
<address />
</opener>
<p>some text - might be anything
<pb facs="someotherstring" />
more text or possibly xml. </p>
</body>
</text>
每个“pb”标签都表示文本中的分页符。所以我基本上需要遍历这个文本,每次我点击“pb”标签时,我都需要创建一个新页面,任何跟随“pb”标签的文本或xml都是该新页面的一部分,直到我点击下一个“pb”标签(前一个过程重复)。通常我只需要使用一些XQuery来执行.SelectNodes()来获取该类型的所有内容。这里的问题是“pb”标签没有WRAP跟随它的文本;它总是一个封闭的标签(意味着它永远不会有任何子元素)。所以我不能只获得“pb”标签的所有子元素,因为没有。我需要获得所有兄弟姐妹(以及任何自由形式的文本)BETWEEN“pb”标签和下一个“pb”标签(或者在最后一个“pb”标签,该标签和结束时)身体“标签”。我完全不知道如何开始。真正的挑战发挥作用的是每个“pb”标签可能不是直接的兄弟。有时,“pb”可能存在于另一个元素中(例如“p”标记,因此由于缺少另一个“pb”标记的更好术语而使其成为“侄子”)或者它可能是直接的兄弟。
请注意,这是利用标准化的文本编码(称为TEI),所以我不能只决定将“pb”标签包裹在构成页面的元素周围;这不符合标准。
更新 我已经尝试过实施@ Tomalak的解决方案而且我已经接近了一点但仍然没有。为了节省时间,我复制了我的代码和我正在测试的实际文件的示例,以帮助说明问题。
虽然代码无例外地触发,但它实际上并未向我提供两个“pb”标记之间的内容。我最终得到一个NodeList,其中第一个条目为空,第二个条目包含“body”标签中的所有xml /文本(而不仅仅是第一个“pb”标签和第二个“pb”之间的text / xml标签)。希望有人可以对此有所了解。代码如下。
C#代码
var pages = text.SelectNodes ("//tei:pb", _xmlns);
StringBuilder pageText = new StringBuilder ();
//Letters.Domain.Objects.FileInfo file = null;
for (int i=0; i < pages.Count; i++) {
pageCount++;
XmlNode page = pages [i];
//string pageNumber = this.GetAttributeValue ("n", page);
string facsId = this.GetAttributeValue ("facs", page).Substring(1);
//get the text between page breaks
var path = "//node()[not(self::tei:pb) and ancestor::tei:text/tei:body and count(preceding::tei:pb) = " + i + "]";
var pbText = pages [i].SelectNodes (path, _xmlns);
//Do something with the content of pbText
}
示例XML
<?xml version="1.0" encoding="UTF-8"?>
<?oxygen RNGSchema="http://www.tei-c.org/release/xml/tei/custom/schema/relaxng/tei_all.rng" type="xml"?>
<TEI xmlns="http://www.tei-c.org/ns/1.0">
<teiHeader xml:id="L1916_2004">
<!--header stuff goes here-->
</teiHeader>
<facsimile>
<graphic xml:id="L1916_2004_img_1" url="1e84e52835597bd4d2229709a02f0a93.jpg"/>
<graphic xml:id="L1916_2004_img_2" url="b6ad122b71088b7eec53262038ffa443.jpg"/>
</facsimile>
<text type="letter"><!-- issue to resolve with a previous process? -->
<body>
<pb n="5008" facs="#L1916_2004_img_1"/>
<opener>
<address><addrLine>Zossen</addrLine></address>
<dateline><date>Xmas Day 1915</date></dateline>
<salute>Dear old pet,</salute>
</opener>
<p>No letter from you yet. I am so <lb/> lonely today - you remember last year <lb/> in 8 Hartstonge St. with the little ones running <lb/> wild — why do you grip me so much <lb/> & why are my thoughts so cluttered around <lb/> you? Mollie write me, I do so want your <lb/> sympathy, my work is so hard, and difficulties <lb/> so many - but of course if I had you here to <lb/> tell all my worries to, things would run <lb/> smoothly - I trust you and all at home
<pb n="5009" facs="#L1916_2004_img_2"/>
are enjoying yourselves, even now as I write <lb/> this letter —</p>
<p>I cant write more now, fondest love <lb/> Kiss them all for me </p>
<closer>
<salute> Yours ever </salute>
<signed><unclear>Cara</unclear></signed>
</closer>
</body>
</text>
</TEI>
答案 0 :(得分:1)
你可以这样做:
var breaks = doc.SelectNodes("//pb");
for (var i = 0; i < breaks.Count; i++) {
var path = "../node()[count(preceding-sibling::pb) = " + (i + 1) + "]";
var contents = breaks[i].SelectNodes(path);
// so something with contents
}
这假定文档中的所有<pb>
个节点都是A)兄弟姐妹,B)出现在内容的根级别,就像您的示例XML所示。
(在问题更新后编辑):没有做出这种假设的解决方案适用于preceding
轴,而不是preceding-sibling
:
var doc = new XmlDocument();
doc.LoadXml(xmlString);
var nsman = new System.Xml.XmlNamespaceManager(doc.NameTable);
nsman.AddNamespace("tei", "http://www.tei-c.org/ns/1.0");
var breaks = doc.SelectNodes("//tei:pb", nsman);
for (var i = 0; i < breaks.Count; i++) {
var path = String.Format(@"
//node()[
ancestor::tei:body
and not(self::tei:pb)
and count(preceding::tei:pb) = {0}
]
", i + 1);
var contents = breaks[i].SelectNodes(path, nsman);
// so something with contents
}
请注意,对于i == 1
,这将返回负责<p>
所属的<pb>
。这个<p>
自然会包含以下页面的一些内容。 <pb>
之前的初始文本节点也将返回以及(单独),但是在这里您没有立即指出它是从<p>
内部获取的。选择你的毒药。
玩弄表情。您可以尝试添加not(.//pb)
以排除包含<pb>
的元素,并仅查找其内容。
答案 1 :(得分:0)
尝试将XML解析为XDocument对象,比如说X。
然后调用X.Root.Value。