在不同的Xml元素之间查找文本

时间:2015-11-02 16:27:36

标签: c# xml

我有一个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 &#x2014; why do you grip me so much <lb/> &amp; 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 &#x2014;</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>

2 个答案:

答案 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。