13 Haziran 2018

XML Oluştururken (onaltılık değeri 0x1F), geçersiz bir karakter. Hata için çözüm yöntemleri.

Aşağıda ki hata XML oluştururken geçersiz bir karakterin, XML içerisinde olması durumunda meydana gelir.


System.ArgumentException: ' ' (onaltılık değeri 0x1F), geçersiz bir karakter.

Server stack trace: 
  konum: System.Xml.XmlEncodedRawTextWriter.InvalidXmlChar(Int32 ch, Char* pDst, Boolean entitize)
  konum: System.Xml.XmlEncodedRawTextWriter.WriteElementTextBlock(Char* pSrc, Char* pSrcEnd)
  konum: System.Xml.XmlEncodedRawTextWriter.WriteString(String text)
  konum: System.Xml.XmlEncodedRawTextWriterIndent.WriteString(String text)
  konum: System.Xml.XmlWellFormedWriter.WriteString(String text)
  konum: System.Xml.Linq.ElementWriter.WriteElement(XElement e)
  konum: System.Xml.Linq.XElement.WriteTo(XmlWriter writer)
  konum: System.Xml.Linq.XNode.GetXmlString(SaveOptions o)
  konum: System.Xml.Linq.XNode.ToString()


Bu hatayı alabileceğiniz örnek C# kodu



using System;
using System.Xml.Linq;

namespace ConsoleApplication1
{
    class Program
    {
      public static void Main(params string[] args)
        {
            var a = "a" + ((char)0x1F).ToString();
            var b = "b";
            var x = new XElement("root",
                new XElement("a", a),
                new XElement("b", b),
                new XElement("c", "c"),
                new XElement("d", "d")
                );
            Console.WriteLine(x);
        }
   }
}


Eğer kod içerisinde bu karakteri silerek çözmek istiyorsanız, replace metodu ile direk çözebilirsiniz.

Örnek:
new XElement("a", a.Replace(((char)0x1F).ToString(),""))


Öncelikle bu hatanın en genel sebebi dışarıdan kopyalanıp programımıza yapıştırılan yazılarda oluyor.
Genellikle kopyalanan metin içersinde gereksiz bir karakter olabiliyor. Bu hata eğer çok nadir gözüküyorsa, bunu database üzerinden bulup değiştirmek daha kolay olacaktır.

Eğer bu değişkenin database üzerinde hangi alanda olduğunu biliyorsanız direk aratıp bulabilirsiniz.

Fakat hatanın hangi alandan kaynaklandığını bulamayacak kadar karmaşık bir yapınız varsa database üzerindeki  tüm alanları tarayabileceğiniz bir sorgu vardır. Bu sorgunun orjinali aşağıdaki adrestedir.
https://social.technet.microsoft.com/wiki/contents/articles/31234.invalid-character-0x1f-error-on-reports-system-center-configuration-manager.aspx

Bizim databasemiz içerisinde çok fazla tablo ve veri varsa yukarıdaki sorgu biraz yavaş kalıyor. Bunun yerine biz tablo ismi vererek sınırlandırabileceğimiz yeni bir sorgu yazalım. Çünkü genel olarak xml oluşturulurken kullandığı tabloları en azında biliyoruz. Bunun için kaynakta belirtilen Prosedürü tekrar modifiye ettim ve yeni sorgu aşağıdaki gibidir.
CREATE PROC SearchAllTablesEx 
( 
@SearchStr nvarchar(100), 
@TableName nvarchar(256)
) 
AS 
BEGIN 
CREATE TABLE #Results (ColumnName nvarchar(370), ColumnValue nvarchar(3630)) 
SET NOCOUNT ON 
declare @ColumnName nvarchar(128), @SearchStr2 nvarchar(110) 
SET @SearchStr2 = QUOTENAME('%' + @SearchStr + '%','''') 
    SET @ColumnName = ''   
    WHILE (@TableName IS NOT NULL) AND (@ColumnName IS NOT NULL) 
    BEGIN
             PRINT @TableName 
             PRINT @ColumnName 
        SET @ColumnName = 
        ( 
            SELECT MIN(QUOTENAME(COLUMN_NAME)) 
            FROM    INFORMATION_SCHEMA.COLUMNS 
            WHERE TABLE_NAME  = PARSENAME(@TableName, 1) 
                AND DATA_TYPE IN ('char', 'varchar', 'nchar', 'nvarchar') 
                AND QUOTENAME(COLUMN_NAME) > @ColumnName 
        ) 
        IF @ColumnName IS NOT NULL 
        BEGIN 
            INSERT INTO #Results 
           EXEC 
            ( 
                'SELECT ''' + @TableName + '.' + @ColumnName + ''', LEFT(' + @ColumnName + ', 3630) 
                FROM ' + @TableName + ' (NOLOCK) ' + 
                ' WHERE ' + @ColumnName + ' LIKE ' + @SearchStr2 
            ) 
        END 
END 
SELECT ColumnName, ColumnValue FROM #Results 
END



Bu prosedürü çalıştırmak için 

DECLARE @searchString AS VARCHAR(50), @tableName VARCHAR(50) 
SET @searchString = Char(0x1F) 
SET @tableName = 'TABLOADI'
EXEC SearchAllTablesEx @searchString, @tableName