2016-01-06 166 views
2

有没有办法选择性地用其他XElement替换XElement内容?用XElement替换XElement内容

我有这样的XML:

<prompt> 
    There is something I want to tell you.[pause=3] 
    You are my favorite caller today.[pause=1] 
    Have a great day! 
</prompt> 

我想呈现为这样:

<prompt> 
    There is something I want to tell you.<break time="3s"/> 
    You are my favorite caller today.<break time="1s"/> 
    Have a great day! 
</prompt> 

我需要更换与实际XElements占位符,但是当我试图改变内容的XElement,.NET当然会逃避所有的尖括号。我明白为什么内容通常需要正确转义,但我需要绕过该行为并将XML直接注入内容。

这是我的代码,否则工作。

MatchCollection matches = Regex.Matches(content, @"\[(\w+)=(\d+)]"); 
foreach (XElement element in voiceXmlDocument.Descendants("prompt")) 
{ 
    if (matches[0] == null) 
     continue; 
    element.Value = element.Value.Replace(matches[0].Value, @"<break time=""5s""/>"); 
} 

这是一项正在进行的工作,所以不要太在意RegEx模式的有效性,因为我将在稍后处理以符合多个条件。这是概念代码的证明,重点在于如所描述的替换占位符。我只在这里包含了迭代和RegEx代码,以说明我需要能够对已经填充了内容的整个文档执行此操作。

回答

1

您可以使用XElement.Parse()方法:

首先,让你的XElement的外部XML,例如,

string outerXml = element.ToString(); 

的你正是这个字符串的工作:

<prompt> 
    There is something I want to tell you.[pause=3] 
    You are my favorite caller today.[pause=1] 
    Have a great day! 
</prompt> 

然后,你可以做你更换

outerXml = outerXml.Replace(matches[0].Value, @"<break time=""5s""/>"); 

然后你就可以解析回:

XElement repElement = XElement.Parse(outerXml); 

最后,替换原来的XElement:

element.ReplaceWith(repElement); 
0

所有这一切的关键是XText,它允许您使用文本作为一个元素。

这是循环:

foreach (XElement prompt in voiceXmlDocument.Descendants("prompt")) 
{ 
    string text = prompt.Value; 
    prompt.RemoveAll(); 
    foreach (string phrase in text.Split('[')) 
    { 
     string[] parts = phrase.Split(']'); 
     if (parts.Length > 1) 
     { 
      string[] pause = parts[0].Split('='); 
      prompt.Add(new XElement("break", new XAttribute("time", pause[1]))); 
      // add a + "s" if you REALLY want it, but then you have to get rid 
      // of it later in some other code. 
     } 
     prompt.Add(new XText(parts[parts.Length - 1])); 
    } 
} 

这是最终的结果

<prompt> 
     There is something I want to tell you.<break time="3" /> 
     You are my favorite caller today.<break time="1" /> 
     Have a great day! 
</prompt> 
0
class Program 
{ 
    static void Main(string[] args) 
    { 
     var xml = 
      @"<prompt>There is something I want to tell you.[pause=3] You are my favorite caller today.[pause=1] Have a great day!</prompt>"; 

     var voiceXmlDocument = XElement.Parse(xml); 

     var pattern = new Regex(@"\[(\w+)=(\d+)]"); 

     foreach (var element in voiceXmlDocument.DescendantsAndSelf("prompt")) 
     { 
      var matches = pattern.Matches(element.Value); 

      foreach (var match in matches) 
      { 
       var matchValue = match.ToString(); 

       var number = Regex.Match(matchValue, @"\d+").Value; 

       var newValue = string.Format(@"<break time=""{0}""/>", number); 

       element.Value = element.Value.Replace(matchValue, newValue); 
      } 

     } 

     Console.WriteLine(voiceXmlDocument.ToString()); 
    } 
} 
0

哦,我的天啊,你们比我预想的快!所以,谢谢你,但是在此期间,我解决了一个稍微不同的方式。这里的代码看起来与因为一旦我得到了它的工作展开之前,我加了一些细节到这一特殊情况:

foreach (XElement element in voiceXmlDocument.Descendants("prompt").ToArray()) 
{ 
    // convert the element to a string and see to see if there are any instances 
    // of pause placeholders in it 
    string elementAsString = element.ToString(); 
    MatchCollection matches = Regex.Matches(elementAsString, @"\[pause=(\d+)]"); 
    if (matches == null || matches.Count == 0) 
     continue; 
    // if there were no matches or an empty set, move on to the next one 

    // iterate through the indexed matches 
    for (int i = 0; i < matches.Count; i++) 
    { 
     int pauseValue = 0; // capture the original pause value specified by the user 
     int pauseMilliSeconds = 1000; // if things go wrong, use a 1 second default 
     if (matches[i].Groups.Count == 2) // the value is expected to be in the second group 
     { 
      // if the value could be parsed to an integer, convert it from 1/8 seconds to milliseconds 
      if (int.TryParse(matches[i].Groups[1].Value, out pauseValue)) 
       pauseMilliSeconds = pauseValue * 125; 
     } 

     // replace the specific match with the new <break> tag content 
     elementAsString = elementAsString.Replace(matches[i].Value, string.Format(@"<break time=""{0}ms""/>", pauseMilliSeconds)); 
    } 

    // finally replace the element by parsing 
    element.ReplaceWith(XElement.Parse(elementAsString)); 
} 
+1

有趣。我有同样的想法。 –

+1

是的,你没有,亚历克斯,如果我没有那么固执,我会转移到别的东西,并检查回答。为了良好的业力,我将把你的回答标记为答案。 –

0

哦,我的天哪,你们是比我预想的更快!

Doh!不管怎样,不妨发布我的解决方案!

foreach (var element in xml.Descendants("prompt")) 
{ 
    Queue<string> pauses = new Queue<string>(Regex.Matches(element.Value, @"\[pause *= *\d+\]") 
     .Cast<Match>() 
     .Select(m => m.Value)); 
    Queue<string> text = new Queue<string>(element.Value.Split(pauses.ToArray(), StringSplitOptions.None)); 
    element.RemoveAll(); 
    while (text.Any()) 
    { 
     element.Add(new XText(text.Dequeue())); 
     if (pauses.Any()) 
      element.Add(new XElement("break", new XAttribute("time", Regex.Match(pauses.Dequeue(), @"\d+")))); 
    } 
} 

对于每个提示元素,正则表达式会匹配所有暂停并将它们放入队列中。

然后使用这些提示分隔内部文本并抓取'其他'文本并将其放入队列中。

使用RemoveAll清除元素中的所有数据,然后迭代您的分隔数据并将其重新添加为适当的数据类型。当您添加新的属性时,您可以使用Regex从原始匹配中获取数字值。