【问题标题】:XDocument Text Node New LineXDocument 文本节点换行
【发布时间】:2011-09-20 12:20:26
【问题描述】:

我正在尝试使用来自 Linq XML 命名空间的 XText 在文本节点中添加换行符。

我有一个包含换行符的字符串,但是我需要弄清楚如何将它们转换为实体字符(即
),而不是仅仅让它们作为换行符出现在 XML 中。

XElement element = new XElement( "NodeName" );
...

string example = "This is a string\nWith new lines in it\n";

element.Add( new XText( example ) );

然后使用XmlTextWriter 写出XElement,这会导致文件包含换行符而不是实体替换。

有没有人遇到过这个问题并找到了解决方案?


编辑:

当我将 XML 加载到似乎不喜欢换行符但接受实体替换的 EXCEL 中时,问题就显现出来了。结果是 EXCEL 中不会显示换行符,除非我将它们替换为 


尼克。

【问题讨论】:

  • 你试过用XCData代替XText吗?
  • 不,我没有 - 我会试一试,但我预测 EXCEL 无论如何都不会喜欢它!

标签: c# xml newline linq-to-xml


【解决方案1】:

作弊:

        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.CheckCharacters = false;
        settings.NewLineChars = "
";
        XmlWriter writer = XmlWriter.Create(..., settings);
        element.WriteTo(writer);
        writer.Flush();

更新:

完整的程序

using System;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        XElement element = new XElement( "NodeName" );
        string example = "This is a string\nWith new lines in it\n";
        element.Add( new XText( example ) );

        XmlWriterSettings settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.CheckCharacters = false;
        settings.NewLineChars = "
";
        XmlWriter writer = XmlWriter.Create(Console.Out, settings);
        element.WriteTo(writer);
        writer.Flush();
    }
}
}

输出:

C:\Users\...\\ConsoleApplication1\bin\Release>ConsoleApplication1.exe
<?xml version="1.0" encoding="ibm850"?>&#10;<NodeName>This is a string&#10;With new lines in it&#10;</NodeName>

【讨论】:

  • 这似乎没有替换文本节点中的换行符。
  • 完美。解决了手头的问题。
【解决方案2】:

对于任何标准 XML 解析器,实体 &amp;#10; 和换行符之间没有区别,因为它们是一回事。

为了说明这一点,下面的代码表明它们是同一件事:

string s1 = "<root>Test&#10;Test2</root>";
string s2 = "<root>Test\nTest2</root>";

XDocument doc1 = XDocument.Parse(s1);
XDocument doc2 = XDocument.Parse(s2);

Console.WriteLine(doc1.ToString());
Console.WriteLine(doc2.ToString());

【讨论】:

【解决方案3】:

XmlTextWriter 负责输出转义的实体。所以如果你这样做,例如:

        using (XmlTextWriter w = new XmlTextWriter("test.xml", Encoding.UTf8))
        {
            w.WriteString("&#x10;");
        }

您还将在 text.xml &amp;amp;#x10 中得到一个转义的 & 符号输出,这是您不想要的。您希望保持 &amp;amp;#x10; 序列的原始状态。

我建议的解决方案是创建一个新的 StreamWriter 实现,能够检测像“&amp;amp;#x10;”这样的转义字符串:

    // A StreamWriter that does not escape &#10; characters
    public class NonXmlEscapingStreamWriter : StreamWriter
    {
        private const string AmpToken = "amp";
        private int _bufferState = 0; // used to keep state

        // add other ctors overloads if needed
        public NonXmlEscapingStreamWriter(string path)
            : base(path)
        {
        }

        // NOTE this code is based on the assumption that StreamWriter
        // only overrides these 4 Write functions, which is true today but could change in the future
        // and also on the assumption that the XmlTextWrite writes escaped values in a specific WriteXX calls sequence
        public override void Write(char value)
        {
            if (value == '&')
            {
                if (_bufferState == 0)
                {
                    _bufferState++;
                    return; // hold it
                }
                else
                {
                    _bufferState = 0;
                }
            }
            else if (value == ';')
            {
                if (_bufferState > 1)
                {
                    _bufferState++;
                    return;
                }
                else
                {
                    Write('&'); // release what's been held
                    Write(AmpToken);
                    _bufferState = 0;
                }
            }
            else if (value == '\n') // detect non escaped \n
            {
                base.Write("&#10;");
                return;
            }
            base.Write(value);
        }

        public override void Write(string value)
        {
            if (_bufferState > 0)
            {
                if (value == AmpToken)
                {
                    _bufferState++;
                    return; // hold it
                }
                else
                {
                    Write('&'); // release what's been held
                    _bufferState = 0;
                }
            }
            base.Write(value);
        }

        public override void Write(char[] buffer, int index, int count)
        {
            if (_bufferState > 2)
            {
                _bufferState = 0;
                base.Write('&'); // release this anyway
                string replace;
                if ((buffer != null) && ((replace = GetReplaceLength(buffer, index, count)) != null))
                {
                    base.Write(replace);
                    base.Write(buffer, index + replace.Length, count - replace.Length);
                    return;
                }
                else
                {
                    base.Write(AmpToken); // release this
                    base.Write(';'); // release this
                }
            }
            base.Write(buffer, index, count);
        }

        public override void Write(char[] buffer)
        {
            Write(buffer, 0, buffer != null ? buffer.Length : 0);
        }

        private string GetReplaceLength(char[] buffer, int index, int count)
        {
            // this is specific to the 10 character but could be adapted
            const string token = "#10;";
            if ((index + count) < token.Length)
                return null;

            // we test the char array to avoid string allocations
            for(int i = 0; i < token.Length; i++)
            {
                if (buffer[index + i] != token[i])
                    return null;
            }
            return token;
        }
    }

你可以这样使用它:

    using (XmlTextWriter w = new XmlTextWriter(new NonXmlEscapingStreamWriter("test.xml")))
    {
        element.WriteTo(w);
    }

注意:虽然它能够检测孤独的 \n 序列,但我建议您确保所有 \n 在您的原始文本中都被转义,因此,您需要在实际输出 xml 之前将 \n 替换为 &amp;amp;#x10; ,像这样:

string example = "This is a string&#x10;With new lines in it&#x10;";

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多