การใช้ StringWriter สำหรับ XML Serialization


99

ฉันกำลังค้นหาวิธีง่ายๆในการทำให้เป็นอนุกรมวัตถุ (ใน C # 3)

ฉัน googled ตัวอย่างบางส่วนและคิดสิ่งต่างๆเช่น:

MemoryStream memoryStream = new MemoryStream ( );
XmlSerializer xs = new XmlSerializer ( typeof ( MyObject) );
XmlTextWriter xmlTextWriter = new XmlTextWriter ( memoryStream, Encoding.UTF8 );
xs.Serialize ( xmlTextWriter, myObject);
string result = Encoding.UTF8.GetString(memoryStream .ToArray());

หลังจากอ่านคำถามนี้ฉันถามตัวเองทำไมไม่ใช้ StringWriter? ดูเหมือนง่ายกว่ามาก

XmlSerializer ser = new XmlSerializer(typeof(MyObject));
StringWriter writer = new StringWriter();
ser.Serialize(writer, myObject);
serializedValue = writer.ToString();

ปัญหาอีกประการหนึ่งคือตัวอย่างแรกสร้าง XML ที่ฉันไม่สามารถเขียนลงในคอลัมน์ XML ของ SQL Server 2005 DB ได้

คำถามแรกคือ: มีเหตุผลที่ทำไมฉันไม่ควรใช้ StringWriter เพื่อทำให้เป็นอนุกรมของวัตถุเมื่อฉันต้องการเป็นสตริงในภายหลัง? ฉันไม่พบผลลัพธ์โดยใช้ StringWriter เมื่อ googling

ประการที่สองคือ: หากคุณไม่ควรทำกับ StringWriter (ไม่ว่าด้วยเหตุผลใดก็ตาม) วิธีใดจะเป็นวิธีที่ดีและถูกต้อง?


ส่วนที่เพิ่มเข้าไป:

ตามที่ได้กล่าวไปแล้วโดยทั้งสองคำตอบฉันจะเข้าสู่ปัญหา XML เป็น DB ต่อไป

เมื่อเขียนไปยังฐานข้อมูลฉันได้รับข้อยกเว้นดังต่อไปนี้:

System.Data.SqlClient.SqlException: การแยกวิเคราะห์ XML: บรรทัดที่ 1 อักขระ 38 ไม่สามารถสลับการเข้ารหัส

สำหรับสตริง

<?xml version="1.0" encoding="utf-8"?><test/>

ฉันเอาสตริงที่สร้างจาก XmlTextWriter และใส่เป็น xml ที่นั่น อันนี้ใช้งานไม่ได้ (ไม่ได้ใส่เข้าไปใน DB ด้วยตนเอง)

หลังจากนั้นฉันลองใช้การแทรกด้วยตนเอง (เพียงแค่เขียน INSERT INTO ... ) ด้วยการเข้ารหัส = "utf-16" ซึ่งก็ล้มเหลวเช่นกัน การลบการเข้ารหัสใช้งานได้โดยสิ้นเชิง หลังจากผลลัพธ์นั้นฉันเปลี่ยนกลับไปใช้รหัส StringWriter และ voila - มันใช้งานได้

ปัญหา: ฉันไม่เข้าใจว่าทำไม

ที่ Christian Hayter: จากการทดสอบเหล่านั้นฉันไม่แน่ใจว่าฉันต้องใช้ utf-16 เพื่อเขียนไปยัง DB การตั้งค่าการเข้ารหัสเป็น UTF-16 (ในแท็ก xml) จะไม่ทำงานหรือ


1
ฉันจะเล่าประสบการณ์ส่วนตัว SQL Server ยอมรับ UTF-16 เท่านั้นและหากคุณส่งผ่านสิ่งอื่นใดคุณจะอยู่ในความเมตตาของตัวแยกวิเคราะห์ XML ของเซิร์ฟเวอร์ SQL และความพยายามในการแปลงข้อมูล แทนที่จะพยายามหาทางหลอกฉันแค่ส่ง UTF-16 โดยตรงซึ่งจะได้ผลเสมอ
Christian Hayter

คุณเขียนสิ่งนี้ลงในฐานข้อมูลได้อย่างไร? คุณส่งสตริงหรืออาร์เรย์ของไบต์หรือเขียนไปยังสตรีม? หากเป็นสองรูปแบบหลังคุณต้องตรวจสอบให้แน่ใจว่าการเข้ารหัสที่คุณประกาศนั้นตรงกับการเข้ารหัสข้อมูลไบนารีของคุณจริง
Jon Skeet

วุ้ย. คู่มือลองทำเป็นแบบสอบถามใน MS SQL Management Studio การพยายาม "coded" ถูกเขียนลงในสตริงซึ่งจะถูกส่งต่อไปยัง O / R Mapper ซึ่งเขียนเป็นสตริง (เท่าที่ฉันจะติดตามได้) อันที่จริงฉันส่งสตริงที่สร้างขึ้นในสองตัวอย่างที่ให้ไว้ในคำถามของฉัน
StampedeXV

FYI ถึงผู้อ่าน - ใกล้รายการซ้ำ: stackoverflow.com/questions/384974/…และstackoverflow.com/questions/3760788/…
ziesemer

1
ฉันกำลังเปลี่ยนคำตอบที่ยอมรับเพราะฉันเชื่อว่ามันตอบคำถามของฉันได้จริง แม้ว่าคำตอบอื่น ๆ จะช่วยให้ฉันทำงานต่อไปได้ แต่สำหรับ Stackoverflow ฉันคิดว่าคำตอบของโซโลมอนจะช่วยให้ผู้อื่นเข้าใจสิ่งที่เกิดขึ้นได้ดีขึ้น [Disclaimer]: ฉันหาเวลาตรวจสอบคำตอบไม่ได้จริงๆ
StampedeXV

คำตอบ:


1

<TL; DR>ปัญหาค่อนข้างง่ายจริงๆแล้ว: คุณไม่ได้จับคู่การเข้ารหัสที่ประกาศ (ในการประกาศ XML) กับประเภทข้อมูลของพารามิเตอร์อินพุต หากคุณเพิ่ม<?xml version="1.0" encoding="utf-8"?><test/>เข้าไปในสตริงด้วยตนเองการประกาศSqlParameterเป็นประเภทSqlDbType.XmlหรือSqlDbType.NVarCharจะทำให้คุณเกิดข้อผิดพลาด "ไม่สามารถเปลี่ยนการเข้ารหัส" จากนั้นเมื่อแทรกด้วยตนเองผ่าน T-SQL เนื่องจากคุณเปลี่ยนการเข้ารหัสที่ประกาศให้เป็นutf-16คุณได้แทรกVARCHARสตริงอย่างชัดเจน(ไม่ได้ขึ้นต้นด้วยตัวพิมพ์ใหญ่ "N" ดังนั้นการเข้ารหัสแบบ 8 บิตเช่น UTF-8) และไม่ใช่NVARCHARสตริง (ขึ้นต้นด้วยตัวพิมพ์ใหญ่ "N" ดังนั้นการเข้ารหัสแบบ 16 บิต UTF-16 LE)

การแก้ไขควรทำได้ง่ายๆดังนี้:

  1. ในกรณีแรกเมื่อเพิ่มคำประกาศที่ระบุ encoding="utf-8" : อย่าเพิ่มการประกาศ XML
  2. ในกรณีที่สองเมื่อเพิ่มคำประกาศที่ระบุencoding="utf-16": อย่างใดอย่างหนึ่ง
    1. อย่าเพิ่มการประกาศ XML หรือ
    2. เพียงเพิ่ม "N" ลงในประเภทพารามิเตอร์อินพุต: SqlDbType.NVarCharแทนSqlDbType.VarChar:-) (หรืออาจเปลี่ยนไปใช้SqlDbType.Xml )

(คำตอบโดยละเอียดอยู่ด้านล่าง)


คำตอบทั้งหมดนี้ซับซ้อนเกินไปและไม่จำเป็น (โดยไม่คำนึงถึงคะแนน 121 และ 184 สำหรับคำตอบของ Christian และ Jon ตามลำดับ) พวกเขาอาจให้รหัสที่ใช้งานได้ แต่ไม่มีใครตอบคำถามได้จริง ปัญหาคือไม่มีใครเข้าใจคำถามอย่างแท้จริงซึ่งท้ายที่สุดแล้วเกี่ยวกับวิธีการทำงานของประเภทข้อมูล XML ใน SQL Server ไม่มีอะไรเทียบกับคนฉลาดทั้งสองอย่างชัดเจน แต่คำถามนี้แทบไม่มีอะไรเกี่ยวข้องกับการทำให้เป็นอนุกรมกับ XML การบันทึกข้อมูล XML ลงใน SQL Server นั้นง่ายกว่าที่กล่าวโดยนัยที่นี่มาก

ไม่สำคัญว่าจะสร้าง XML อย่างไรตราบใดที่คุณปฏิบัติตามกฎของวิธีสร้างข้อมูล XML ใน SQL Server ฉันมีคำอธิบายอย่างละเอียดมากขึ้น (รวมถึงโค้ดตัวอย่างการทำงานเพื่อแสดงให้เห็นถึงประเด็นที่ระบุไว้ด้านล่าง) ในคำตอบสำหรับคำถามนี้: วิธีแก้ข้อผิดพลาด "ไม่สามารถสลับการเข้ารหัส" เมื่อใส่ XML ลงใน SQL Serverแต่พื้นฐานคือ

  1. การประกาศ XML เป็นทางเลือก
  2. ประเภทข้อมูล XML จะเก็บสตริงเป็น UCS-2 / UTF-16 LE เสมอ
  3. หาก XML ของคุณคือ UCS-2 / UTF-16 LE คุณจะ:
    1. ส่งผ่านข้อมูลเป็นNVARCHAR(MAX)หรือXML/ SqlDbType.NVarChar(maxsize = -1) หรือSqlDbType.Xmlหรือถ้าใช้สตริงลิเทอรัลข้อมูลนั้นจะต้องขึ้นต้นด้วยตัวพิมพ์ใหญ่ "N"
    2. หากระบุการประกาศ XML จะต้องเป็น "UCS-2" หรือ "UTF-16" (ไม่มีความแตกต่างจริงที่นี่)
  4. หาก XML ของคุณเข้ารหัสแบบ 8 บิต (เช่น "UTF-8" / "iso-8859-1" / "Windows-1252") คุณจะ:
    1. จำเป็นต้องระบุการประกาศ XML หากการเข้ารหัสแตกต่างจากโค้ดเพจที่ระบุโดยค่าเริ่มต้นการเรียงฐานข้อมูล
    2. คุณต้องส่งผ่านข้อมูลเป็นVARCHAR(MAX)/ SqlDbType.VarChar(maxsize = -1) หรือหากใช้สตริงลิเทอรัลจะต้องไม่ขึ้นต้นด้วยตัวพิมพ์ใหญ่ "N"
    3. ไม่ว่าจะใช้การเข้ารหัสแบบ 8 บิตแบบใดก็ตาม "การเข้ารหัส" ที่ระบุไว้ในการประกาศ XML จะต้องตรงกับการเข้ารหัสไบต์ที่แท้จริง
    4. การเข้ารหัส 8 บิตจะถูกแปลงเป็น UTF-16 LE โดยประเภทข้อมูล XML

ด้วยประเด็นที่ระบุไว้ข้างต้นในใจและเนื่องจากสตริงใน. NET มักจะเป็นUTF-16 LE / UCS-2 LE (ไม่มีความแตกต่างระหว่างสิ่งเหล่านี้ในแง่ของการเข้ารหัส) เราสามารถตอบคำถามของคุณได้:

มีเหตุผลไหมที่ฉันไม่ควรใช้ StringWriter เพื่อทำให้เป็นอนุกรมของวัตถุเมื่อฉันต้องการเป็นสตริงในภายหลัง?

ไม่StringWriterรหัสของคุณดูเหมือนจะใช้ได้ (อย่างน้อยฉันก็ไม่เห็นปัญหาใด ๆ ในการทดสอบแบบ จำกัด ของฉันโดยใช้บล็อกโค้ดที่ 2 จากคำถาม)

การตั้งค่าการเข้ารหัสเป็น UTF-16 (ในแท็ก xml) จะไม่ทำงานหรือ

ไม่จำเป็นต้องระบุการประกาศ XML เมื่อขาดหายไปการเข้ารหัสจะถือว่าเป็น UTF-16 LE หากคุณส่งสตริงไปยัง SQL Server เป็นNVARCHAR(เช่นSqlDbType.NVarChar) หรือXML(เช่นSqlDbType.Xml) การเข้ารหัสจะถือว่าเป็นโค้ดเพจเริ่มต้น 8 บิตหากส่งผ่านเป็นVARCHAR(เช่นSqlDbType.VarChar) หากคุณมีอักขระ ASCII ที่ไม่ได้มาตรฐาน (เช่นค่า 128 ขึ้นไป) และกำลังส่งผ่านในฐานะVARCHARคุณจะเห็น "?" สำหรับอักขระ BMP และ "??" สำหรับอักขระเสริมเป็น SQL Server จะแปลงสตริง UTF-16 จาก. NET เป็นสตริง 8 บิตของโค้ดเพจของฐานข้อมูลปัจจุบันก่อนที่จะแปลงกลับเป็น UTF-16 / UCS-2 แต่คุณไม่ควรได้รับข้อผิดพลาดใด ๆ

ในทางกลับกันถ้าคุณระบุการประกาศ XML คุณต้องส่งผ่านไปยัง SQL Server โดยใช้ประเภทข้อมูล 8 บิตหรือ 16 บิตที่ตรงกัน ดังนั้นถ้าคุณมีการประกาศที่ระบุว่าการเข้ารหัสเป็นทั้ง UCS 2 หรือ UTF-16 แล้วคุณจะต้องผ่านในฐานะหรือSqlDbType.NVarChar SqlDbType.Xmlหรือหากคุณมีการประกาศที่ระบุว่าการเข้ารหัสเป็นหนึ่งในตัวเลือกที่ 8 บิต (เช่นUTF-8, Windows-1252, iso-8859-1ฯลฯ ) แล้วคุณจะต้องSqlDbType.VarCharผ่านในขณะที่ ความล้มเหลวในการจับคู่การเข้ารหัสที่ประกาศกับประเภทข้อมูล SQL Server 8 หรือ 16 บิตที่เหมาะสมจะส่งผลให้เกิดข้อผิดพลาด "ไม่สามารถสลับการเข้ารหัส" ที่คุณได้รับ

ตัวอย่างเช่นการใช้StringWriterรหัสซีเรียลไลเซชันตามของคุณฉันเพียงแค่พิมพ์สตริงผลลัพธ์ของ XML และใช้ใน SSMS ดังที่คุณเห็นด้านล่างการประกาศ XML รวมอยู่ด้วย (เนื่องจากStringWriterไม่มีตัวเลือกให้OmitXmlDeclarationชอบXmlWriter) ซึ่งจะไม่มีปัญหาตราบใดที่คุณส่งสตริงเป็นประเภทข้อมูล SQL Server ที่ถูกต้อง:

-- Upper-case "N" prefix == NVARCHAR, hence no error:
DECLARE @Xml XML = N'<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';
SELECT @Xml;
-- <string>Test ሴ😸</string>

อย่างที่คุณเห็นมันยังจัดการกับอักขระที่อยู่นอกเหนือ ASCII มาตรฐานเนื่องจากเป็นจุดรหัส BMP U + 1234 และ😸เป็นจุดรหัสอักขระเสริม U + 1F638 อย่างไรก็ตามสิ่งต่อไปนี้:

-- No upper-case "N" prefix on the string literal, hence VARCHAR:
DECLARE @Xml XML = '<?xml version="1.0" encoding="utf-16"?>
<string>Test ሴ😸</string>';

ส่งผลให้เกิดข้อผิดพลาดต่อไปนี้:

Msg 9402, Level 16, State 1, Line XXXXX
XML parsing: line 1, character 39, unable to switch the encoding

Ergo นอกเหนือจากคำอธิบายทั้งหมดแล้วคำตอบที่สมบูรณ์สำหรับคำถามเดิมของคุณคือ:

SqlDbType.VarCharคุณได้อย่างชัดเจนผ่านสตริงเป็น เปลี่ยนไปใช้SqlDbType.NVarCharและจะใช้งานได้โดยไม่จำเป็นต้องทำตามขั้นตอนเพิ่มเติมในการลบการประกาศ XML สิ่งนี้เป็นที่ต้องการมากกว่าการเก็บรักษาSqlDbType.VarCharและลบการประกาศ XML เนื่องจากโซลูชันนี้จะป้องกันข้อมูลสูญหายเมื่อ XML มีอักขระ ASCII ที่ไม่ได้มาตรฐาน ตัวอย่างเช่น:

-- No upper-case "N" prefix on the string literal == VARCHAR, and no XML declaration:
DECLARE @Xml2 XML = '<string>Test ሴ😸</string>';
SELECT @Xml2;
-- <string>Test ???</string>

อย่างที่คุณเห็นไม่มีข้อผิดพลาดในครั้งนี้ แต่ตอนนี้มีข้อมูลสูญหาย🙀


ฉันคิดว่าฉันเป็นสาเหตุของคำตอบที่ซับซ้อนนี้เนื่องจากโดยพื้นฐานแล้วฉันมีคำถามสองข้อในหนึ่งข้อ ฉันชอบคำตอบที่กระชับของคุณมากและจะลองใช้ในครั้งต่อไปที่ฉันต้องจัดเก็บ XML ใน DB ดังนั้นถ้าฉันเห็นถูกต้อง: คุณได้อธิบายถึงความท้าทายในการจัดเก็บ XML ไปยัง DB Jon Skeet สรุปปัญหาเกี่ยวกับการใช้ StringWriter เมื่อทำงานกับ XML (ยกเว้น UTF-16) และ Christian Hayter เป็นวิธีที่ดีในการทำงานกับมัน
StampedeXV

@StampedeXV ฉันอัปเดตคำตอบของฉัน (การเปลี่ยนแปลงเล็กน้อยเพื่อความชัดเจน + สิ่งใหม่ ๆ เพื่อแสดงให้เห็นประเด็นที่ดีขึ้น) หวังว่าจะชัดเจนขึ้นในขณะนี้แม้ว่าคำตอบทั้งสองจะดีในตัวเอง แต่ก็ไม่จำเป็นต้องตอบคำถามของคุณ แต่อย่างใด พวกเขาจัดการกับการทำให้เป็นอนุกรม XML ใน C # / .NET แต่คำถามนี้เกี่ยวกับการบันทึก XML ใน SQL Server พวกเขาให้ข้อมูลที่ควรทราบและอาจเป็นรหัสที่ดีกว่าที่คุณให้ไว้ในตอนแรก แต่ไม่มีทั้งสองอย่าง (หรือข้อมูลอื่น ๆ ที่นี่) ไม่ได้อยู่ในหัวข้ออย่างแท้จริง แต่นี่ไม่ใช่สิ่งที่มีการจัดทำเอกสารไว้อย่างดีจึงทำให้เกิดความสับสน
Solomon Rutzky

@StampedeXV การแก้ไขของฉันสมเหตุสมผลหรือไม่? ฉันเพิ่งเพิ่มส่วนสรุปที่ด้านบนซึ่งอาจจะชัดเจนกว่า เรื่องสั้นขนาดยาว: เว้นแต่จะมีสิ่งอื่นเกิดขึ้นที่คุณไม่ได้ใส่รายละเอียดในคำถามดูเหมือนว่ารหัสของคุณจะถูกต้อง 99% และอาจได้รับการแก้ไขด้วยการเพิ่มตัวพิมพ์ใหญ่เพียงตัวเดียว " N ". ไม่จำเป็นต้องมีการเข้ารหัสพิเศษใด ๆ และรหัสของ Christian ก็ดี แต่การทดสอบของฉันแสดงให้เห็นว่ามันส่งคืนการทำให้เป็นอนุกรมเหมือนกับบล็อกโค้ดที่ 2 ของคุณยกเว้นของคุณใส่ CRLF หลังจากการประกาศ XML ฉันเดิมพันที่คุณเปลี่ยนไปหรือSqlDbType.NVarChar Xml
Solomon Rutzky

ยังคงพยายามหาเวลาตรวจสอบตัวเอง แน่นอนว่าฟังดูมีเหตุผล แต่ไม่แน่ใจว่าจะเพียงพอที่จะเปลี่ยนคำตอบที่ยอมรับได้
StampedeXV

218

ปัญหาอย่างหนึ่งStringWriterคือโดยค่าเริ่มต้นจะไม่อนุญาตให้คุณตั้งค่าการเข้ารหัสที่โฆษณา - ดังนั้นคุณสามารถจบลงด้วยเอกสาร XML ที่โฆษณาการเข้ารหัสเป็น UTF-16 ซึ่งหมายความว่าคุณต้องเข้ารหัสเป็น UTF-16 หากคุณ เขียนลงในไฟล์ ฉันมีชั้นเรียนเล็ก ๆ ที่จะช่วยได้:

public sealed class StringWriterWithEncoding : StringWriter
{
    public override Encoding Encoding { get; }

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

หรือถ้าคุณต้องการเพียง UTF-8 (ซึ่งเป็นสิ่งที่ฉันมักต้องการ):

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

สำหรับสาเหตุที่คุณไม่สามารถบันทึก XML ของคุณลงในฐานข้อมูลได้คุณจะต้องให้รายละเอียดเพิ่มเติมเกี่ยวกับสิ่งที่เกิดขึ้นเมื่อคุณพยายามหากคุณต้องการให้เราสามารถวินิจฉัย / แก้ไขได้


ฉันลงรายละเอียดเพิ่มเติมสำหรับปัญหาฐานข้อมูลตอนนี้ ดูคำถาม
StampedeXV

4
น่าเศร้าที่StringWriterไม่ได้คำนึงถึงการเข้ารหัส แต่ก็ไม่น้อยไปกว่ากันขอบคุณสำหรับวิธีการเล็ก ๆ น้อย ๆ ที่ดี :)
Chau

2
และ "การแยกวิเคราะห์ XML: บรรทัดที่ 1 อักขระ 38 ไม่สามารถเปลี่ยนการเข้ารหัสได้" สามารถแก้ไขได้โดย "settings.Indent = false; settings.OmitXmlDeclaration = false;"
MGE

ฉันมักจะแก้ปัญหานี้ได้โดยใช้ a MemoryStreamและ a StreamWriterกับการเข้ารหัสที่ถูกต้อง StreamWriter เป็นTextWriter (ชนิดที่XmlWriter.Createคาดว่า) กับการเข้ารหัสที่ปรับแต่งหลังจากทั้งหมด
Nyerguds

2
@Nyerguds: ดังนั้นสร้างแพ็คเกจ Nuget ด้วยสิ่งนี้แล้วมันก็ง่ายเสมอที่จะได้รับ ฉันอยากจะทำแบบนั้นมากกว่าที่จะประนีประนอมกับความสามารถในการอ่านโค้ดซึ่งโดยพื้นฐานแล้วเกี่ยวกับข้อกำหนดอื่น ๆ
Jon Skeet

126

เมื่อจัดลำดับเอกสาร XML เป็นสตริง. NET ต้องตั้งค่าการเข้ารหัสเป็น UTF-16 สตริงจะถูกเก็บไว้เป็น UTF-16 ภายในดังนั้นนี่เป็นการเข้ารหัสเพียงอย่างเดียวที่เหมาะสม หากคุณต้องการจัดเก็บข้อมูลในการเข้ารหัสอื่นคุณใช้อาร์เรย์ไบต์แทน

SQL Server ทำงานบนหลักการที่คล้ายกัน สตริงใด ๆ ที่ส่งผ่านเข้าไปในxmlคอลัมน์ต้องเข้ารหัสเป็น UTF-16 SQL Server จะปฏิเสธสตริงใด ๆ ที่การประกาศ XML ไม่ได้ระบุ UTF-16 หากไม่มีการประกาศ XML แสดงว่ามาตรฐาน XML กำหนดให้ค่าเริ่มต้นเป็น UTF-8 ดังนั้น SQL Server จะปฏิเสธเช่นกัน

โปรดคำนึงถึงสิ่งนี้ต่อไปนี้เป็นวิธียูทิลิตี้สำหรับการแปลง

public static string Serialize<T>(T value) {

    if(value == null) {
        return null;
    }

    XmlSerializer serializer = new XmlSerializer(typeof(T));

    XmlWriterSettings settings = new XmlWriterSettings()
    {
        Encoding = new UnicodeEncoding(false, false), // no BOM in a .NET string
        Indent = false,
        OmitXmlDeclaration = false
    };

    using(StringWriter textWriter = new StringWriter()) {
        using(XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) {
            serializer.Serialize(xmlWriter, value);
        }
        return textWriter.ToString();
    }
}

public static T Deserialize<T>(string xml) {

    if(string.IsNullOrEmpty(xml)) {
        return default(T);
    }

    XmlSerializer serializer = new XmlSerializer(typeof(T));

    XmlReaderSettings settings = new XmlReaderSettings();
    // No settings need modifying here

    using(StringReader textReader = new StringReader(xml)) {
        using(XmlReader xmlReader = XmlReader.Create(textReader, settings)) {
            return (T) serializer.Deserialize(xmlReader);
        }
    }
}

ดูคำถามเพิ่มเติม ฉันไม่เข้าใจผลการทดสอบของฉันดูเหมือนว่าจะขัดแย้งกับคำพูดของคุณที่ DB ต้องการ / รับ / ต้องการ UTF-16 เสมอ
StampedeXV

9
คุณไม่จำเป็นต้องเข้ารหัสเป็น UTF-16 แต่คุณต้องตรวจสอบให้แน่ใจว่าการเข้ารหัสที่คุณใช้นั้นตรงกับสิ่งที่StringWriterคาดหวัง ดูคำตอบของฉัน รูปแบบการจัดเก็บข้อมูลภายในไม่เกี่ยวข้องกับที่นี่
Jon Skeet

ตกลงว่าฉันเข้าใจ ในตัวอย่างใหม่ของฉัน: การปล่อยการเข้ารหัสออกไปอย่างสมบูรณ์ทำให้ DB ตัดสินใจด้วยตัวเองว่าจะใช้การเข้ารหัสใดนั่นคือเหตุผลที่ใช้งานได้ ตอนนี้ฉันเข้าใจถูกไหม
StampedeXV

1
@SteveC: ขออภัยความผิดพลาดของฉัน ฉันแปลงรหัสจาก VB ด้วยมือซึ่งNothingสามารถแปลงเป็นประเภทใดก็ได้โดยปริยาย ฉันได้แก้ไขDeserializeรหัสแล้ว Serializeเตือนจะต้องเป็นสิ่งที่ Resharper เท่านั้นคอมไพเลอร์ในตัวเองไม่ได้คัดค้านและมันเป็นกฎหมายที่จะทำ
Christian Hayter

1
ต่อความคิดเห็นของ Jon Skeet ไม่จำเป็นต้องใช้ UTF-16 โปรดดูstackoverflow.com/a/8998183/751158สำหรับตัวอย่างที่เป็นรูปธรรมที่แสดงให้เห็นถึงสิ่งนี้
ziesemer

20

ก่อนอื่นระวังการหาตัวอย่างเก่า ๆ คุณพบสิ่งที่ใช้XmlTextWriterซึ่งเลิกใช้แล้วเมื่อ. NET 2.0 XmlWriter.Createควรใช้แทน

นี่คือตัวอย่างของการทำให้วัตถุเป็นอนุกรมลงในคอลัมน์ XML:

public void SerializeToXmlColumn(object obj)
{
    using (var outputStream = new MemoryStream())
    {
        using (var writer = XmlWriter.Create(outputStream))
        {
            var serializer = new XmlSerializer(obj.GetType());
            serializer.Serialize(writer, obj);
        }

        outputStream.Position = 0;
        using (var conn = new SqlConnection(Settings.Default.ConnectionString))
        {
            conn.Open();

            const string INSERT_COMMAND = @"INSERT INTO XmlStore (Data) VALUES (@Data)";
            using (var cmd = new SqlCommand(INSERT_COMMAND, conn))
            {
                using (var reader = XmlReader.Create(outputStream))
                {
                    var xml = new SqlXml(reader);

                    cmd.Parameters.Clear();
                    cmd.Parameters.AddWithValue("@Data", xml);
                    cmd.ExecuteNonQuery();
                }
            }
        }
    }
}

2
ฉันสามารถโหวตได้เพียงครั้งเดียว แต่สิ่งนี้สมควรเป็นคำตอบอันดับต้น ๆ ที่นี่ ท้ายที่สุดแล้วมันไม่สำคัญว่าจะประกาศหรือใช้การเข้ารหัสใดตราบเท่าที่XmlReaderสามารถแยกวิเคราะห์ได้ จะถูกส่งแยกวิเคราะห์ล่วงหน้าไปยังฐานข้อมูลจากนั้น DB ไม่จำเป็นต้องรู้อะไรเกี่ยวกับการเข้ารหัสอักขระ - UTF-16 หรืออย่างอื่น โดยเฉพาะอย่างยิ่งโปรดทราบว่าการประกาศ XML จะไม่คงอยู่กับข้อมูลในฐานข้อมูลด้วยซ้ำไม่ว่าจะใช้วิธีใดในการแทรก โปรดอย่าทำให้เสียโดยการเรียกใช้ XML ผ่านการแปลงเพิ่มเติมดังที่แสดงในคำตอบอื่น ๆ ที่นี่และที่อื่น ๆ
ziesemer

1
public static T DeserializeFromXml<T>(string xml)
{
    T result;
    XmlSerializerFactory serializerFactory = new XmlSerializerFactory();
    XmlSerializer serializer =serializerFactory.CreateSerializer(typeof(T));

    using (StringReader sr3 = new StringReader(xml))
    {
        XmlReaderSettings settings = new XmlReaderSettings()
        {
            CheckCharacters = false // default value is true;
        };

        using (XmlReader xr3 = XmlTextReader.Create(sr3, settings))
        {
            result = (T)serializer.Deserialize(xr3);
        }
    }

    return result;
}

-1

อาจถูกครอบคลุมที่อื่น แต่การเปลี่ยนบรรทัดการเข้ารหัสของแหล่ง XML เป็น 'utf-16' ทำให้สามารถแทรก XML ลงในประเภท xml'data ของ SQL Server ได้

using (DataSetTableAdapters.SQSTableAdapter tbl_SQS = new DataSetTableAdapters.SQSTableAdapter())
{
    try
    {
        bodyXML = @"<?xml version="1.0" encoding="UTF-8" standalone="yes"?><test></test>";
        bodyXMLutf16 = bodyXML.Replace("UTF-8", "UTF-16");
        tbl_SQS.Insert(messageID, receiptHandle, md5OfBody, bodyXMLutf16, sourceType);
    }
    catch (System.Data.SqlClient.SqlException ex)
    {
        Console.WriteLine(ex.Message);
        Console.ReadLine();
    }
}

ผลลัพธ์คือข้อความ XML ทั้งหมดถูกแทรกลงในฟิลด์ชนิดข้อมูล "xml" แต่บรรทัด "ส่วนหัว" จะถูกลบออก สิ่งที่คุณเห็นในบันทึกผลลัพธ์เป็นเพียง

<test></test>

การใช้วิธีการทำให้เป็นลำดับตามที่อธิบายไว้ในรายการ "ตอบแล้ว" เป็นวิธีการรวมส่วนหัวเดิมในช่องเป้าหมาย แต่ผลลัพธ์คือข้อความ XML ที่เหลืออยู่ใน<string></string>แท็กXML

อะแดปเตอร์ตารางในโค้ดเป็นคลาสที่สร้างขึ้นโดยอัตโนมัติโดยใช้ Visual Studio 2013 "Add New Data Source: wizard พารามิเตอร์ห้าตัวในการแม็พวิธีการแทรกไปยังฟิลด์ในตาราง SQL Server


2
แทนที่? นี่มันเฮฮา
mgilberties

2
อย่างจริงจัง - อย่าทำเช่นนี้ เคย. จะเป็นอย่างไรหากฉันต้องการรวมร้อยแก้วใน xml ของฉันที่กล่าวถึง "UTF-8" - คุณเพิ่งเปลี่ยนข้อมูลของฉันเป็นสิ่งที่ฉันไม่ได้พูด!
Tim Abell

2
ขอขอบคุณที่ชี้ให้เห็นข้อผิดพลาดในรหัส แทนที่จะใช้ bodyXML.Replace ("UTF-8", "UTF-16") ควรมีโค้ดที่เน้นที่ส่วนหัว XML โดยเปลี่ยน UTF-8 เป็น UTF-16 สิ่งที่ฉันพยายามจะชี้ให้เห็นจริงๆคือการทำการเปลี่ยนแปลงนี้ในส่วนหัวของ XML ต้นทางจากนั้นสามารถแทรกเนื้อหาของ XML ลงในบันทึกตาราง SQL โดยใช้ฟิลด์ชนิดข้อมูล XML และส่วนหัวจะถูกตัดออก ด้วยเหตุผลที่ฉันจำไม่ได้ในตอนนี้ (สี่ปีที่แล้ว!) และใช่ความผิดพลาดโง่ ๆ โดยใช้ 'แทนที่' มันเกิดขึ้น.
DLG
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.