<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>
보시다시피 이번에는 오류가 없지만 이제 데이터 손실이 있습니다 🙀.