किसी ऑब्जेक्ट को UTF-8 XML के रूप में .NET में सीरियल करना


112

उचित वस्तु निपटान संक्षिप्तता के लिए हटा दिया गया है, लेकिन मैं हैरान हूँ अगर यह स्मृति में UTF-8 के रूप में एक वस्तु को एन्कोड करने का सबसे सरल तरीका है। वहाँ एक आसान तरीका है वहाँ नहीं है?

var serializer = new XmlSerializer(typeof(SomeSerializableObject));

var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);

serializer.Serialize(streamWriter, entry);

memoryStream.Seek(0, SeekOrigin.Begin);
var streamReader = new StreamReader(memoryStream, System.Text.Encoding.UTF8);
var utf8EncodedXml = streamReader.ReadToEnd();


1
मैं उलझन में हूँ ... डिफ़ॉल्ट कूटबन्धन UTF-8 नहीं है?
फ्लाक

@flq, हां डिफॉल्ट UTF-8 है, हालांकि यह बहुत मायने नहीं रखता क्योंकि वह इसे एक स्ट्रिंग में वापस पढ़ रहा है इसलिए utf8EncodedXmlUTF-16 है।
जॉन हन्ना

1
@ गैरी, क्या आप स्पष्ट कर सकते हैं, क्योंकि जॉन स्कीट और मैं विभिन्न सवालों के जवाब दे रहे हैं। क्या आप ऑब्जेक्ट को UTF-8 के रूप में क्रमांकित करना चाहते हैं, या क्या आप XML स्ट्रिंग चाहते हैं जो खुद को UTF-8 घोषित करता है, और इसलिए बाद में UTF-8 में एन्कोड किए जाने पर सही घोषणा होगी? (जिस मामले में सबसे सरल तरीका कोई घोषणा नहीं है, क्योंकि यह UTF-8 और UTF-16 दोनों के लिए मान्य है)।
जॉन हन्ना

@ वापस पढ़ना, मेरे प्रश्न में अस्पष्टता है। मैंने इसे ज्यादातर डिबगिंग उद्देश्यों के लिए एक स्ट्रिंग में आउटपुट किया था। व्यवहार में, मैं संभवतः बाइट स्ट्रीमिंग करूंगा, या तो डिस्क पर या HTTP पर जो आपके उत्तर को मेरी समस्या के लिए अधिक प्रासंगिक बनाता है। मुख्य समस्या जो मैंने XML में UTF-8 की घोषणा की थी, लेकिन अधिक सटीक होने के लिए मुझे एक स्ट्रिंग के मध्यस्थ से बचना चाहिए ताकि मैं वास्तविक निर्भरता / UTF-8 बाइट्स को एक मंच पर निर्भर होने के बजाय (मुझे लगता है) जारी रखें एन्कोडिंग।
गैरी श्टलर

जवाबों:


55

आपके कोड को मेमोरी में UTF-8 प्राप्त नहीं होता है क्योंकि आप इसे फिर से एक स्ट्रिंग में पढ़ते हैं, इसलिए अब यह UTF-8 में नहीं है, लेकिन UTF-16 में वापस आ गया है (हालांकि आदर्श रूप से उच्च स्तर पर स्ट्रिंग पर विचार करने के लिए सबसे अच्छा है किसी भी एन्कोडिंग को छोड़कर, जब ऐसा करने के लिए मजबूर किया जाता है)।

वास्तविक UTF-8 ऑक्टेट प्राप्त करने के लिए:

var serializer = new XmlSerializer(typeof(SomeSerializableObject));

var memoryStream = new MemoryStream();
var streamWriter = new StreamWriter(memoryStream, System.Text.Encoding.UTF8);

serializer.Serialize(streamWriter, entry);

byte[] utf8EncodedXml = memoryStream.ToArray();

मैंने वही निपटान छोड़ दिया है जो आपने छोड़ा है। मैं निम्नलिखित का पक्ष लेता हूं (सामान्य निपटान के साथ छोड़ दिया गया है):

var serializer = new XmlSerializer(typeof(SomeSerializableObject));
using(var memStm = new MemoryStream())
using(var  xw = XmlWriter.Create(memStm))
{
  serializer.Serialize(xw, entry);
  var utf8 = memStm.ToArray();
}

जो कि जटिलता की एक समान मात्रा है, लेकिन यह दर्शाता है कि प्रत्येक चरण में कुछ और करने के लिए एक उचित विकल्प है, जिसमें से सबसे अधिक दबाव मेमोरी के अलावा कहीं और को क्रमबद्ध करना है, जैसे कि फ़ाइल, टीसीपी / आईपी धारा, डेटाबेस, आदि सभी में, यह वास्तव में वह क्रिया नहीं है।


4
इसके अलावा। यदि आप BOM को दबाना चाहते हैं तो आप उपयोग कर सकते हैं XmlWriter.Create(memoryStream, new XmlWriterSettings { Encoding = new UTF8Encoding(false) })
ony

अगर किसी को (मेरे जैसे) को जॉन शो जैसे एक्सएमएल को पढ़ने की जरूरत है, तो मेमोरी स्ट्रीम को 0 पर रिपीट करना याद रखें, अन्यथा आपको "रूट एलिमेंट गायब है" कहते हुए एक अपवाद मिलेगा। तो ऐसा करें: memStm.Position = 0; XmlReader xmlReader = XmlReader.Create (memStm)
सुधांशु मिश्रा

276

नहीं, आप StringWriterमध्यवर्ती से छुटकारा पाने के लिए उपयोग कर सकते हैं MemoryStream। हालांकि, एक्सएमएल में मजबूर करने के लिए आप एक उपयोग करने की आवश्यकता StringWriterहै जो ओवरराइड करता है Encodingसंपत्ति:

public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding => Encoding.UTF8;
}

या यदि आप अभी तक C # 6 का उपयोग नहीं कर रहे हैं:

public class Utf8StringWriter : StringWriter
{
    public override Encoding Encoding { get { return Encoding.UTF8; } }
}

फिर:

var serializer = new XmlSerializer(typeof(SomeSerializableObject));
string utf8;
using (StringWriter writer = new Utf8StringWriter())
{
    serializer.Serialize(writer, entry);
    utf8 = writer.ToString();
}

स्पष्ट रूप से आप Utf8StringWriterएक अधिक सामान्य वर्ग में प्रवेश कर सकते हैं जो इसके निर्माता में किसी भी एन्कोडिंग को स्वीकार करता है - लेकिन मेरे अनुभव में UTF-8 अब तक सबसे अधिक आवश्यक "कस्टम" एन्कोडिंग है StringWriter:)

अब जैसा कि जॉन हैना कहते हैं, यह अभी भी आंतरिक रूप से UTF-16 होगा, लेकिन संभवत: आप इसे किसी बिंदु पर किसी अन्य चीज़ में पास करने जा रहे हैं, इसे बाइनरी डेटा में परिवर्तित करने के लिए ... उस बिंदु पर आप उपरोक्त स्ट्रिंग का उपयोग कर सकते हैं, इसे UTF-8 बाइट में परिवर्तित करें, और सब कुछ ठीक हो जाएगा - क्योंकि XML घोषणा "एन्कोफ -8" को एन्कोडिंग के रूप में निर्दिष्ट करेगी।

संपादित करें: इस काम को दिखाने के लिए एक छोटा लेकिन पूर्ण उदाहरण:

using System;
using System.Text;
using System.IO;
using System.Xml.Serialization;

public class Test
{    
    public int X { get; set; }

    static void Main()
    {
        Test t = new Test();
        var serializer = new XmlSerializer(typeof(Test));
        string utf8;
        using (StringWriter writer = new Utf8StringWriter())
        {
            serializer.Serialize(writer, t);
            utf8 = writer.ToString();
        }
        Console.WriteLine(utf8);
    }


    public class Utf8StringWriter : StringWriter
    {
        public override Encoding Encoding => Encoding.UTF8;
    }
}

परिणाम:

<?xml version="1.0" encoding="utf-8"?>
<Test xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <X>0</X>
</Test>

"Utf-8" की घोषित एन्कोडिंग पर ध्यान दें जो हम चाहते थे, मुझे विश्वास है।


2
जब आप StringWriter पर एन्कोडिंग पैरामीटर को ओवरराइड करते हैं तब भी यह लिखित डेटा को StringBuilder पर भेजता है, इसलिए यह अभी भी UTF-16 है। और स्ट्रिंग केवल कभी भी UTF-16 हो सकती है।
जॉन हन्ना

3
@ जॉन: क्या आपने इसे आज़माया है? मेरे पास है, और यह काम करता है। यह घोषित एन्कोडिंग है जो यहां महत्वपूर्ण है; स्पष्ट रूप से आंतरिक रूप से स्ट्रिंग अभी भी UTF-16 है, लेकिन जब तक यह द्विआधारी में परिवर्तित नहीं होता है (जो किसी भी एन्कोडिंग का उपयोग कर सकता है, जिसमें UTF-8 भी शामिल है)। TextWriter.Encodingसंपत्ति एक्सएमएल serializer द्वारा प्रयोग किया जाता है कि कौन सी एन्कोडिंग नाम दस्तावेज़ के भीतर ही निर्दिष्ट करने के लिए निर्धारित करने के लिए।
जॉन स्कीट

2
@ जॉन: और घोषित एन्कोडिंग क्या थी? मेरे अनुभव में, इस तरह के सवाल वास्तव में करने की कोशिश कर रहे हैं - एक XML दस्तावेज़ बनाएं जो खुद को UTF-8 में होने की घोषणा करता है। जैसा कि आप कहते हैं, पाठ को किसी भी एन्कोडिंग में होने तक विचार करना सबसे अच्छा है जब तक आपको ज़रूरत नहीं है ... लेकिन जैसा कि एक्सएमएल दस्तावेज़ एक एन्कोडिंग की घोषणा करता है , वह कुछ ऐसा है जिस पर आपको विचार करने की आवश्यकता है।
जॉन स्कीट

2
@ गैरी, सबसे सरल मैं अभी सोच सकता हूं कि मेरे जवाब में दूसरा उदाहरण लेना है, लेकिन जब आप XmlWriterऐसा करते हैं तो फैक्टरी विधि के साथ ऐसा करते हैं जो एक XmlWriterSettingsवस्तु लेता है , और OmitXmlDeclarationसंपत्ति सेट होती है true
जॉन हन्ना

4
+1 आपका Utf8StringWriterसमाधान बेहद अच्छा और साफ है
एड्रियानो कारनेइरो

17

विरासत का उपयोग करते हुए बहुत अच्छा जवाब, बस इनिशियलाइज़र को ओवरराइड करना याद रखें

public class Utf8StringWriter : StringWriter
{
    public Utf8StringWriter(StringBuilder sb) : base (sb)
    {
    }
    public override Encoding Encoding { get { return Encoding.UTF8; } }
}

धन्यवाद, मुझे यह विकल्पों में से सबसे अधिक सुरुचिपूर्ण लगता है
प्रोकोरस

5

मुझे यह ब्लॉग पोस्ट मिला जो समस्या को बहुत अच्छी तरह से समझाता है, और कुछ अलग समाधानों को परिभाषित करता है:

(मृत लिंक हटा दिया गया)

मैंने इस विचार के लिए समझौता किया है कि इसे करने का सबसे अच्छा तरीका यह है कि मेमोरी में XML घोषणा को पूरी तरह से छोड़ दिया जाए। यह वास्तव में वैसे भी उस समय UTF-16 है, लेकिन XML घोषणा तब तक सार्थक नहीं लगती जब तक इसे किसी विशेष एन्कोडिंग वाली फ़ाइल में नहीं लिखा गया हो; और तब भी घोषणा की आवश्यकता नहीं है। यह कम से कम deserialization को तोड़ने के लिए प्रतीत नहीं होता है।

जैसा कि @ जों हन्ना का उल्लेख है, यह इस तरह से बनाए गए एक XmlWriter के साथ किया जा सकता है:

XmlWriter writer = XmlWriter.Create (output, new XmlWriterSettings() { OmitXmlDeclaration = true });
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.