用XElement替换XElement内容

时间:2016-01-06 20:18:51

标签: c# .net xml xelement

有没有办法选择性地用其他XElements替换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代码,以说明我需要能够对已经填充了内容的整个文档执行此操作。

5 个答案:

答案 0 :(得分:2)

您可以使用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);

答案 1 :(得分: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>

答案 2 :(得分: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());
    }
}

答案 3 :(得分: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));
}

答案 4 :(得分:0)

  噢,天哪,你们比我想象的要快!

卫生署!不管怎样,不妨发布我的解决方案!

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从原始匹配中获取数字值。