현재 개체를 직렬화하는 쉬운 방법을 찾고 있습니다 (C # 3).
몇 가지 예를 검색하여 다음과 같은 결과를 얻었습니다.
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이 SQL Server 2005 DB의 XML 열에 쓸 수 없다는 것입니다.
첫 번째 질문은 다음과 같습니다. 나중에 문자열로 필요할 때 StringWriter를 사용하여 개체를 직렬화하면 안되는 이유가 있습니까? 인터넷 검색을 할 때 StringWriter를 사용하여 결과를 찾지 못했습니다.
두 번째는 물론입니다. 어떤 이유로 든 StringWriter를 사용하지 말아야한다면 어떤 방법이 좋을까요?
덧셈:
두 답변에서 이미 언급했듯이 XML to DB 문제에 대해 더 자세히 살펴 보겠습니다.
데이터베이스에 쓸 때 다음 예외가 발생했습니다.
System.Data.SqlClient.SqlException : XML 구문 분석 : 줄 1, 문자 38, 인코딩을 전환 할 수 없습니다.
문자열
<?xml version="1.0" encoding="utf-8"?><test/>
XmlTextWriter에서 만든 문자열을 가져 와서 거기에 xml로 넣었습니다. 이것은 작동하지 않았습니다 (DB에 수동으로 삽입하지 않음).
그 후 수동 삽입 (INSERT INTO … 작성)을 encoding = “utf-16″으로 시도했지만 실패했습니다. 그런 다음 인코딩을 완전히 제거했습니다. 그 결과 나는 StringWriter 코드로 다시 전환하고 짜잔-작동했습니다.
문제 : 나는 그 이유를 정말로 이해하지 못합니다.
at Christian Hayter : 이러한 테스트를 통해 DB에 쓰기 위해 utf-16을 사용해야할지 모르겠습니다. 인코딩을 UTF-16 (xml 태그에서)으로 설정하면 작동하지 않습니까?
답변
<TL; DR> 문제는 사실 다소 간단합니다. 선언 된 인코딩 (XML 선언에서)이 입력 매개 변수의 데이터 유형과 일치하지 않습니다. 수동으로 추가 한 경우 <?xml version="1.0" encoding="utf-8"?><test/>
문자열로, 다음은 선언 SqlParameter
형으로 SqlDbType.Xml
또는 SqlDbType.NVarChar
당신에게 “인코딩을 전환 할 수 없습니다”오류를 줄 것이다. 그런 다음 T-SQL을 통해 수동으로 삽입 할 때 선언 된 인코딩을로 전환했기 때문에 문자열 utf-16
을 명확하게 삽입했습니다 VARCHAR
(대문자 “N”접두어가 없으므로 UTF-8과 같은 8 비트 인코딩). NVARCHAR
문자열이 아닙니다 (대문자 “N”이 접두사로 붙으므로 16 비트 UTF-16 LE 인코딩).
수정은 다음과 같이 간단해야합니다.
- 첫 번째 경우에는 다음과 같은 선언을 추가 할 때
encoding="utf-8"
XML 선언을 추가하지 마십시오. - 두 번째 경우에는 다음과 같은 선언을 추가 할 때
encoding="utf-16"
:- 단순히 XML 선언을 추가하지 마십시오. 또는
- 입력 매개 변수 유형에 “N”을 추가하기 만하면됩니다 :
SqlDbType.NVarChar
대신SqlDbType.VarChar
🙂 (또는를 사용하여 전환 할 수도 있습니다SqlDbType.Xml
)
(자세한 답변은 아래 참조)
여기에있는 모든 답변은 지나치게 복잡하고 불필요합니다 (각각 Christian의 답변과 Jon의 답변에 대한 121 개 및 184 개의 찬성 투표에 관계없이). 작동하는 코드를 제공 할 수 있지만 실제로 질문에 대답하는 사람은 없습니다. 문제는 아무도 진정으로 질문을 이해하지 못했다는 것입니다. 궁극적으로 SQL Server의 XML 데이터 유형이 어떻게 작동하는지에 대한 것입니다. 이 두 명의 똑똑한 사람을 상대로 한 것은 아니지만이 질문은 XML로 직렬화하는 것과는 거의 관련이 없습니다. XML 데이터를 SQL Server에 저장하는 것은 여기에 암시 된 것보다 훨씬 쉽습니다.
SQL Server에서 XML 데이터를 만드는 방법에 대한 규칙을 따르는 한 XML이 어떻게 생성되는지는 실제로 중요하지 않습니다. 이 질문에 대한 대답에 대한 자세한 설명 (아래에 설명 된 요점을 설명하는 작업 예제 코드 포함)이 있습니다. XML을 SQL Server에 삽입 할 때 “인코딩을 전환 할 수 없습니다”오류를 해결하는 방법 이지만 기본 사항은 다음과 같습니다.
- XML 선언은 선택 사항입니다.
- XML 데이터 유형은 문자열을 항상 UCS-2 / UTF-16 LE로 저장합니다.
- XML이 UCS-2 / UTF-16 LE이면 다음을 수행합니다.
- 데이터를
NVARCHAR(MAX)
또는XML
/SqlDbType.NVarChar
(maxsize = -1) 또는SqlDbType.Xml
으로 전달하거나 문자열 리터럴을 사용하는 경우 대문자 “N”접두사를 붙여야합니다. - XML 선언을 지정하는 경우 “UCS-2″또는 “UTF-16″이어야합니다 (실제 차이는 없음).
- 데이터를
- XML이 8 비트로 인코딩 된 경우 (예 : “UTF-8″/ “iso-8859-1″/ “Windows-1252”) 다음을 수행합니다.
- 인코딩이 데이터베이스의 기본 데이터 정렬에 지정된 코드 페이지와 다른 경우 XML 선언을 지정해야합니다.
- 데이터를
VARCHAR(MAX)
/SqlDbType.VarChar
(maxsize = -1) 로 전달해야합니다 . 또는 문자열 리터럴을 사용하는 경우 대문자 “N”을 접두사로 사용 하지 않아야 합니다 . - 어떤 8 비트 인코딩이 사용 되든 XML 선언에 명시된 “인코딩”은 바이트의 실제 인코딩과 일치해야합니다.
- 8 비트 인코딩은 XML 데이터 유형에 의해 UTF-16 LE로 변환됩니다.
점은 염두에 위에서 설명한, 함께 하고 주어진 .NET에서 문자열이 있음을 항상 UTF-16 LE / UCS-2 LE, 우리는 당신의 질문에 대답 할 수 있습니다 (인코딩 측면에서 그 사이에는 차이가 없다)
나중에 문자열로 필요할 때 StringWriter를 사용하여 Object를 직렬화하면 안되는 이유가 있습니까?
아니요, 귀하의 StringWriter
코드는 괜찮은 것 같습니다 (적어도 질문의 두 번째 코드 블록을 사용하는 제한된 테스트에서 문제가 없음).
인코딩을 UTF-16 (xml 태그에서)으로 설정하면 작동하지 않습니까?
XML 선언을 제공 할 필요는 없습니다. 누락 된 경우 문자열을 NVARCHAR
(예 SqlDbType.NVarChar
) 또는 XML
(예 SqlDbType.Xml
) 로 SQL Server에 전달 하면 인코딩이 UTF-16 LE로 간주됩니다 . 인코딩은 VARCHAR
(예 🙂 로 전달되는 경우 기본 8 비트 코드 페이지로 간주됩니다 SqlDbType.VarChar
. 비표준 ASCII 문자 (즉, 값 128 이상)가 있고로 전달되는 경우 VARCHAR
“?”가 표시 될 수 있습니다. BMP 문자 및 “??” SQL Server와 같은 보조 문자의 경우 UTF-16 문자열을 .NET에서 현재 데이터베이스 코드 페이지의 8 비트 문자열로 변환 한 후 다시 UTF-16 / UCS-2로 변환합니다. 그러나 오류가 발생해서는 안됩니다.
반면에 XML 선언을 지정하는 경우 일치하는 8 비트 또는 16 비트 데이터 형식을 사용하여 SQL Server에 전달 해야합니다 . 당신이 선언은 인코딩이 없다는 그래서 만약 하나 UCS-2, UTF-16, 당신은 있어야 로 전달 SqlDbType.NVarChar
또는 SqlDbType.Xml
. 또는, 당신은 인코딩 (즉, 8 비트 옵션 중 하나입니다한다는 선언이있는 경우 UTF-8
, Windows-1252
, iso-8859-1
, 등), 당신은 해야한다 등의 전달을 SqlDbType.VarChar
. 선언 된 인코딩을 적절한 8 비트 또는 16 비트 SQL Server 데이터 형식과 일치시키지 않으면 “인코딩을 전환 할 수 없습니다”오류가 발생합니다.
예를 들어, StringWriter
기반 직렬화 코드를 사용하여 XML의 결과 문자열을 인쇄하고 SSMS에서 사용했습니다. 아래에서 볼 수 있듯이 XML 선언이 포함되어 있습니다 ( 좋아 StringWriter
하는 옵션이 없기 때문에 ). 올바른 SQL Server 데이터 유형으로 문자열을 전달하는 한 문제가되지 않습니다.OmitXmlDeclaration
XmlWriter
-- 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>
보시다시피, ሴ
BMP 코드 포인트 U + 1234이고 ?
보조 문자 코드 포인트 U + 1F638 인 경우 표준 ASCII 이상의 문자도 처리합니다 . 그러나 다음은 다음과 같습니다.
-- 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에 비표준 ASCII 문자가 포함 된 경우 데이터 손실을 방지하므로 XML 선언 을 유지 하고 제거하는 것보다 선호 됩니다. 예를 들면 :
-- 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>
보시다시피 이번에는 오류가 없지만 이제 데이터 손실이 있습니다 ?.
답변
한 가지 문제 StringWriter
는 기본적으로 광고하는 인코딩을 설정할 수 없다는 것입니다. 따라서 인코딩을 UTF-16으로 광고하는 XML 문서로 끝날 수 있습니다. 즉, 다음과 같은 경우 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을 데이터베이스에 저장할 수없는 이유에 관해서는 우리가 진단 / 수정할 수 있도록하려면 시도했을 때 발생한 일에 대한 자세한 정보를 제공해야합니다.
답변
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);
}
}
}
답변
우선, 오래된 사례를 찾아야합니다. 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();
}
}
}
}
}
답변
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;
}
답변
다른 곳에서 다루었을 수도 있지만 XML 소스의 인코딩 줄을 ‘utf-16’으로 변경하면 XML을 SQL Server ‘xml’데이터 형식에 삽입 할 수 있습니다.
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’데이터 유형 필드에 삽입되지만 ‘header’행은 제거됩니다. 결과 기록에서 보는 것은
<test></test>
“Answered”항목에 설명 된 직렬화 방법을 사용하는 것은 대상 필드에 원래 헤더를 포함하는 방법이지만 결과적으로 나머지 XML 텍스트는 XML <string></string>
태그로 묶입니다 .
코드의 테이블 어댑터는 Visual Studio 2013 “새 데이터 원본 추가 : 마법사를 사용하여 자동으로 빌드 된 클래스입니다. Insert 메서드에 대한 5 개의 매개 변수는 SQL Server 테이블의 필드에 매핑됩니다.