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 iの場合)はutf-16でエンコードされており、変更できないためです。
では、エンコーディング属性を「windows-1250」に設定してこのxmlを簡単に作成するにはどうすればよいですか?このエンコードでエンコードする必要はありません。指定された属性を持っている必要があります。
編集:.Net 2.0である必要があるため、新しいフレームワーク要素は使用できません。
解決
適切なエンコーディングで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; }
}
}
(この質問は似ていますが、まったく重複していません。)
編集:コメントに回答するには:StringWriterWithEncodingを XmlWriter.Create に渡しますa> StringBuilderの代わりに、最後にToString()を呼び出します。
他のヒント
これがなぜそうなのか、いくつかの追加説明。
文字列は、バイトではなく文字のシーケンスです。文字列自体は「エンコード」されていません。文字列はUnicodeコードポイントとして保存されている文字を使用しているためです。エンコードは文字列レベルで意味をなしません。
エンコードとは、一連のコードポイント(文字)から一連のバイトへのマッピングです(ファイルシステムやメモリなどのバイトベースのシステムに保存するため)。フレームワークでは、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();
}
}
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());
実際に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;
}
文字列を変数に出力し、utf-16への参照をutf-8に置き換えて解決しました(私のアプリはUTF8エンコードが必要でした)。関数を使用しているため、同様のことができます。私は主にVB.netを使用していますが、C#はこのように見えると思います。
return builder.ToString().Replace("utf-16", "utf-8");