web-dev-qa-db-ja.com

XmlWriterを使用して、UTF-16以外のXMLにエンコーディング属性を設定するにはどうすればよいですか?

XmlDocumentを作成する関数があります。

public string CreateOutputXmlString(ICollection<Field> fields)
{
    XmlWriterSettings settings = new XmlWriterSettings();
    settings.Indent = true;
    settings.Encoding = Encoding.GetEncoding("windows-1250");

    StringBuilder builder = new StringBuilder();
    XmlWriter writer = XmlWriter.Create(builder, settings);

    writer.WriteStartDocument();
    writer.WriteStartElement("data");
    foreach (Field field in fields)
    {
        writer.WriteStartElement("item");
        writer.WriteAttributeString("name", field.Id);
        writer.WriteAttributeString("value", field.Value);
        writer.WriteEndElement();
    }
    writer.WriteEndElement();
    writer.Flush();
    writer.Close();

    return builder.ToString();
}

エンコーディングを設定しましたが、XmlWriterを作成した後はutf-16エンコーディングを使用しています。文字列(およびStringBuilderだと思います)はutf-16でエンコードされており、変更できないためです。
それでは、エンコーディング属性を「windows-1250」に設定してこのxmlを簡単に作成するにはどうすればよいですか?このエンコーディングでエンコードする必要はなく、指定された属性を持つ必要があります。

編集:.Net 2.0内にある必要があるため、新しいフレームワーク要素は使用できません。

38
agnieszka

適切なエンコーディングでStringWriterを使用する必要があります。残念ながら、StringWriterではエンコーディングを直接指定できないため、次のようなクラスが必要です。

public sealed class StringWriterWithEncoding : StringWriter
{
    private readonly Encoding encoding;

    public StringWriterWithEncoding (Encoding encoding)
    {
        this.encoding = encoding;
    }

    public override Encoding Encoding
    {
        get { return encoding; }
    }
}

この質問 は似ていますが、まったく同じではありません。)

編集:コメントに答えるには、StringBuilderWithの代わりにStringWriterWithEncodingを XmlWriter.Create に渡し、最後にToString()を呼び出します。

76
Jon Skeet

これがなぜそうであるかについてのいくつかの追加説明。

文字列はバイトではなく文字のシーケンスです。文字列自体は、Unicodeコードポイントとして格納されている文字を使用しているため、「エンコード」されていません。エンコーディングは文字列レベルでSENSEを作成しません。

エンコーディングは、一連のコードポイント(文字)から一連のバイトへのマッピングです(ファイルシステムやメモリなどのバイトベースのシステムでのストレージ用)。フレームワークでは、16ビットのコードポイントをバイトベースのストレージに適合させるなど、やむを得ない理由がない限り、エンコーディングを指定できません。

したがって、XMLをStringBuilderに書き込もうとすると、実際にはXMLの文字シーケンスを構築し、それらを文字のシーケンスとして書き込むので、エンコードは実行されません。したがって、エンコーディングフィールドはありません。

エンコーディングを使用する場合、XmlWriterはストリームに書き込む必要があります。

あなたがMemoryStreamで見つけた解決策について、意図的な攻撃はありませんが、それは単に腕をはためいて熱気を動かすだけです。コードポイントを「windows-1252」でエンコードし、それを解析してコードポイントに戻しています。発生する可能性がある唯一の変更は、windows-1252で定義されていない文字が「?」に変換されることです。プロセスのキャラクター。

私にとって、正しい解決策は次のようなものでしょう。関数の使用目的に応じて、Streamをパラメーターとして関数に渡し、呼び出し元がメモリに書き込むかファイルに書き込むかを決定できるようにすることができます。したがって、次のように記述されます。


        public static void WriteFieldsAsXmlDocument(ICollection fields, Stream outStream)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.Encoding = Encoding.GetEncoding("windows-1250");

            using(XmlWriter writer = XmlWriter.Create(outStream, settings)) {
                writer.WriteStartDocument();
                writer.WriteStartElement("data");
                foreach (Field field in fields)
                {
                    writer.WriteStartElement("item");
                    writer.WriteAttributeString("name", field.Id);
                    writer.WriteAttributeString("value", field.Value);
                    writer.WriteEndElement();
                }
                writer.WriteEndElement();
            }
        }
5
MemoryStream memoryStream = new MemoryStream();
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = Encoding.UTF8;

XmlWriter xmlWriter = XmlWriter.Create(memoryStream, xmlWriterSettings);
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("root", "http://www.timvw.be/ns");
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
xmlWriter.Flush();
xmlWriter.Close();

string xmlString = Encoding.UTF8.GetString(memoryStream.ToArray());

ここから

5
EddiG

私は実際にMemoryStreamの問題を解決しました:

public static string CreateOutputXmlString(ICollection<Field> fields)
        {
            XmlWriterSettings settings = new XmlWriterSettings();
            settings.Indent = true;
            settings.Encoding = Encoding.GetEncoding("windows-1250");

            MemoryStream memStream = new MemoryStream();
            XmlWriter writer = XmlWriter.Create(memStream, settings);

            writer.WriteStartDocument();
            writer.WriteStartElement("data");
            foreach (Field field in fields)
            {
                writer.WriteStartElement("item");
                writer.WriteAttributeString("name", field.Id);
                writer.WriteAttributeString("value", field.Value);
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
            writer.Flush();
            writer.Close();

            writer.Flush();
            writer.Close();

            string xml = Encoding.GetEncoding("windows-1250").GetString(memStream.ToArray());

            memStream.Close();
            memStream.Dispose();

            return xml;
        }
3
agnieszka

文字列を変数に出力し、utf-16への参照をutf-8に置き換えることで解決しました(私のアプリにはUTF8エンコーディングが必要でした)。関数を使用しているので、同様のことができます。私はほとんどVB.netを使用していますが、C#は次のようになると思います。

return builder.ToString().Replace("utf-16", "utf-8");
0
SEFL