Obecnie szukam łatwego sposobu serializacji obiektów (w C # 3).
Wyszukałem w Google kilka przykładów i wymyśliłem coś takiego:
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());
Po przeczytaniu tego pytania zadałem sobie pytanie, dlaczego nie skorzystać z StringWriter? Wydaje się o wiele łatwiejsze.
XmlSerializer ser = new XmlSerializer(typeof(MyObject));
StringWriter writer = new StringWriter();
ser.Serialize(writer, myObject);
serializedValue = writer.ToString();
Innym problemem było to, że pierwszy przykład wygenerował XML, którego nie mogłem po prostu zapisać w kolumnie XML bazy danych SQL Server 2005.
Pierwsze pytanie brzmi: czy istnieje powód, dla którego nie powinienem używać StringWriter do serializacji obiektu, gdy potrzebuję go później jako ciągu? Nigdy nie znalazłem wyniku przy użyciu StringWriter podczas wyszukiwania w Google.
Druga to oczywiście: jeśli nie powinieneś tego robić za pomocą StringWriter (z jakichkolwiek powodów), który byłby dobry i poprawny sposób?
Dodanie:
Jak już wspomniano w obu odpowiedziach, przejdę dalej do problemu XML do bazy danych.
Pisząc do Bazy Danych dostałem następujący wyjątek:
System.Data.SqlClient.SqlException: Analiza XML: wiersz 1, znak 38, nie można zmienić kodowania
Na sznurku
<?xml version="1.0" encoding="utf-8"?><test/>
Wziąłem ciąg utworzony z XmlTextWriter i po prostu umieściłem tam jako xml. Ten nie zadziałał (ani przy ręcznym wstawianiu do bazy danych).
Później próbowałem ręcznego wstawiania (po prostu pisałem INSERT INTO ...) z encoding = "utf-16", co również się nie powiodło. Usunięcie kodowania całkowicie zadziałało. Po tym wyniku wróciłem do kodu StringWriter i voila - zadziałało.
Problem: Naprawdę nie rozumiem, dlaczego.
w Christian Hayter: Z tymi testami nie jestem pewien, czy muszę używać utf-16, aby pisać do DB. Czy wtedy ustawienie kodowania na UTF-16 (w tagu xml) nie zadziała?
źródło
Odpowiedzi:
<TL; DR> Problem jest raczej prosty: nie dopasowujesz zadeklarowanego kodowania (w deklaracji XML) do typu danych parametru wejściowego. Jeśli ręcznie dodano
<?xml version="1.0" encoding="utf-8"?><test/>
do ciągu, a następnie zadeklarowanie plikuSqlParameter
jako typuSqlDbType.Xml
lubSqlDbType.NVarChar
spowodowałoby błąd „nie można zmienić kodowania”. Następnie, podczas ręcznego wstawiania przez T-SQL, ponieważ zmieniłeś zadeklarowane kodowanie nautf-16
, wyraźnie wstawiałeśVARCHAR
ciąg (bez prefiksu wielkiej litery „N”, stąd kodowanie 8-bitowe, takie jak UTF-8) a nieNVARCHAR
ciągiem (poprzedzonym dużą literą „N”, stąd 16-bitowe kodowanie UTF-16 LE).Poprawka powinna być tak prosta, jak:
encoding="utf-8"
: po prostu nie dodawaj deklaracji XML.encoding="utf-16"
: alboSqlDbType.NVarChar
zamiastSqlDbType.VarChar
:-) (lub nawet przełącz się na używanieSqlDbType.Xml
)(Szczegółowa odpowiedź znajduje się poniżej)
Wszystkie odpowiedzi tutaj są zbyt skomplikowane i niepotrzebne (niezależnie od 121 i 184 głosów pozytywnych odpowiednio na odpowiedzi Christiana i Jona). Mogą dostarczyć działający kod, ale żaden z nich tak naprawdę nie odpowiada na pytanie. Problem polega na tym, że nikt tak naprawdę nie zrozumiał pytania, które ostatecznie dotyczy tego, jak działa typ danych XML w SQL Server. Nie ma nic przeciwko tym dwóm wyraźnie inteligentnym ludziom, ale to pytanie nie ma nic wspólnego z serializacją do XML. Zapisywanie danych XML w SQL Server jest znacznie łatwiejsze niż to, co jest tutaj sugerowane.
Nie ma znaczenia, w jaki sposób powstaje XML, o ile przestrzegasz zasad tworzenia danych XML w SQL Server. Mam dokładniejsze wyjaśnienie (w tym działający przykładowy kod ilustrujący punkty przedstawione poniżej) w odpowiedzi na to pytanie: Jak rozwiązać błąd „nie można zmienić kodowania” podczas wstawiania XML do SQL Server , ale podstawy są następujące:
NVARCHAR(MAX)
iXML
/SqlDbType.NVarChar
(MAXSIZE = 1) lubSqlDbType.Xml
, czy za pomocą łańcuch znaków to musi być poprzedzone dużą literę „n”.VARCHAR(MAX)
/SqlDbType.VarChar
(maxsize = -1), lub jeśli używasz literału łańcuchowego, nie możesz poprzedzać go wielką literą „N”.Mając na uwadze powyższe punkty i biorąc pod uwagę, że ciągi znaków w .NET to zawsze UTF-16 LE / UCS-2 LE (nie ma między nimi różnicy w zakresie kodowania), możemy odpowiedzieć na Twoje pytania:
Nie, Twój
StringWriter
kod wydaje się być w porządku (przynajmniej nie widzę żadnych problemów w moich ograniczonych testach z użyciem drugiego bloku kodu z pytania).Podawanie deklaracji XML nie jest konieczne. Gdy go brakuje, zakłada się, że kodowanie to UTF-16 LE, jeśli przekazujesz ciąg do SQL Server jako
NVARCHAR
(tj.SqlDbType.NVarChar
) LubXML
(tjSqlDbType.Xml
.). Zakłada się, że kodowanie jest domyślną 8-bitową stroną kodową, jeśli jest przekazywane jakoVARCHAR
(tjSqlDbType.VarChar
.). Jeśli masz jakieś niestandardowe znaki ASCII (tj. Wartości 128 i więcej) i przekazujesz je jakoVARCHAR
, prawdopodobnie zobaczysz znak „?” dla znaków BMP i „??” dla znaków uzupełniających, ponieważ SQL Server skonwertuje ciąg znaków UTF-16 z .NET na 8-bitowy ciąg strony kodowej bieżącej bazy danych przed konwersją z powrotem na UTF-16 / UCS-2. Ale nie powinieneś otrzymać żadnych błędów.Z drugiej strony, jeśli określisz deklarację XML, musisz przekazać ją do SQL Server przy użyciu pasującego 8-bitowego lub 16-bitowego typu danych. Więc jeśli masz deklarację stwierdzającą, że kodowanie to UCS-2 lub UTF-16, musisz przekazać jako
SqlDbType.NVarChar
lubSqlDbType.Xml
. Albo, jeśli masz deklarację stwierdzającą, że kodowanie jest jedną z opcji 8-bitowych (to znaczyUTF-8
,Windows-1252
,iso-8859-1
itp), to musi przejść w jakSqlDbType.VarChar
. Niezgodność zadeklarowanego kodowania z odpowiednim 8- lub 16-bitowym typem danych programu SQL Server spowoduje wystąpienie błędu „nie można zmienić kodowania”.Na przykład, używając
StringWriter
kodu serializacji opartego na twoim , po prostu wydrukowałem wynikowy ciąg XML i użyłem go w SSMS. Jak widać poniżej, deklaracja XML jest włączone (boStringWriter
nie ma opcji doOmitXmlDeclaration
jakXmlWriter
robi), która nie stanowi żadnego problemu, tak długo, jak przekazać ciąg jako prawidłowy typ danych SQL Server:Jak widać, obsługuje nawet znaki spoza standardowego ASCII, biorąc pod uwagę, że
ሴ
jest to punkt kodowy BMP U + 1234 i😸
jest to dodatkowy punkt kodowy znaków U + 1F638. Jednak następujące:powoduje następujący błąd:
Ergo, pomijając te wszystkie wyjaśnienia, pełne rozwiązanie twojego pierwotnego pytania brzmi:
Wyraźnie przekazałeś ciąg jako
SqlDbType.VarChar
. Przełącz się naSqlDbType.NVarChar
i będzie działać bez konieczności przechodzenia przez dodatkowy krok polegający na usunięciu deklaracji XML. Jest to preferowane rozwiązanie zamiast zachowywaniaSqlDbType.VarChar
i usuwania deklaracji XML, ponieważ to rozwiązanie zapobiega utracie danych, gdy XML zawiera niestandardowe znaki ASCII. Na przykład:Jak widać, tym razem nie ma błędu, ale teraz nastąpiła utrata danych 🙀.
źródło
SqlDbType.NVarChar
lubXml
.Jednym z problemów
StringWriter
jest to, że domyślnie nie pozwala ustawić kodowania, które reklamuje - więc możesz skończyć z dokumentem XML reklamującym jego kodowanie jako UTF-16, co oznacza, że musisz zakodować go jako UTF-16, jeśli zapisz to do pliku. Mam jednak małą klasę do pomocy w tym:Lub jeśli potrzebujesz tylko UTF-8 (co jest wszystkim, czego często potrzebuję):
Jeśli chodzi o powody, dla których nie możesz zapisać pliku XML w bazie danych - musisz podać nam więcej szczegółów na temat tego, co się stało, gdy próbowałeś, jeśli chcesz, abyśmy mogli to zdiagnozować / naprawić.
źródło
StringWriter
nie bierze pod uwagę kodowania, ale nie mniej, dzięki za sprytną małą metodę :)MemoryStream
i aStreamWriter
z odpowiednim kodowaniem.StreamWriter
jest wTextWriter
końcu typem (XmlWriter.Create
oczekiwanym) z konfigurowalnym kodowaniem.Podczas serializacji dokumentu XML do łańcucha .NET należy ustawić kodowanie na UTF-16. Łańcuchy są wewnętrznie przechowywane jako UTF-16, więc jest to jedyne kodowanie, które ma sens. Jeśli chcesz przechowywać dane w innym kodowaniu, zamiast tego użyj tablicy bajtów.
SQL Server działa na podobnej zasadzie; każdy ciąg przekazany do
xml
kolumny musi być zakodowany w formacie UTF-16. SQL Server odrzuci każdy ciąg, w którym deklaracja XML nie określa UTF-16. Jeśli deklaracja XML nie jest obecna, standard XML wymaga, aby była ona domyślnie ustawiona na UTF-8, więc SQL Server również ją odrzuci.Mając to na uwadze, oto kilka użytecznych metod konwersji.
źródło
StringWriter
oczekuje. Zobacz moją odpowiedź. Format pamięci wewnętrznej nie ma tutaj znaczenia.Nothing
jest niejawnie konwertowany na dowolny typ. PoprawiłemDeserialize
kod.Serialize
Ostrzegawczy musi być Resharper-jedyną rzeczą, kompilator na własną rękę nie sprzeciw i jest zgodne z prawem.Przede wszystkim uważaj na stare przykłady. Znalazłeś taki, który używa
XmlTextWriter
, który jest przestarzały od .NET 2.0.XmlWriter.Create
należy użyć zamiast tego.Oto przykład serializacji obiektu do kolumny XML:
źródło
XmlReader
może je przeanalizować. Zostanie wysłany wstępnie przeanalizowany do bazy danych, a wtedy baza danych nie musi nic wiedzieć o kodowaniu znaków - UTF-16 lub innym. W szczególności należy zauważyć, że deklaracje XML nie są nawet utrwalane z danymi w bazie danych, niezależnie od metody ich wstawiania. Proszę nie marnować czasu, uruchamiając XML za pomocą dodatkowych konwersji, jak pokazano w innych odpowiedziach tutaj i gdzie indziej.źródło
Być może zostało to omówione gdzie indziej, ale po prostu zmiana linii kodowania źródła XML na „utf-16” umożliwia wstawienie XML do typu xml'data serwera SQL Server.
W rezultacie cały tekst XML zostanie wstawiony do pola typu danych „xml”, ale wiersz „nagłówka” zostanie usunięty. To, co widzisz w wynikowym rekordzie, jest po prostu
Użycie metody serializacji opisanej we wpisie „Odpowiedzi” jest sposobem na włączenie oryginalnego nagłówka do pola docelowego, ale w rezultacie pozostały tekst XML jest zawarty w
<string></string>
znaczniku XML .Adapter tabeli w kodzie jest klasą automatycznie budowaną przy użyciu kreatora „Dodaj nowe źródło danych:” programu Visual Studio 2013. Pięć parametrów metody Insert jest odwzorowywana na pola w tabeli programu SQL Server.
źródło