代码之家  ›  专栏  ›  技术社区  ›  ΩmegaMan

ExecuteXmlReader在来自SQL Server的特定JSON上失败

  •  0
  • ΩmegaMan  · 技术社区  · 6 年前

    我有一个现有的工作进程,它使用 for JSON 指令。但是在接收到列数据中的特定文本时,会出现 ExecuteXmlReader Read 操作。

    例外

    XmlException:“=”是意外的标记。需要的标记是“;”。 1号线,94号位置

    如果我控制了输出,我很可能会将其放在cdata部分。

    从SQL Server返回的数据

    JSON_F52E2B61-18A1-11d1-B105-00805F49916B
    {"photoId":1000000007,"photoType":"image\/gif","photoUrl":"https:\/\/slack-imgs.com\/?c=1&url=https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2F3o84U9arAYRM73AIvu%2Fgiphy-downsized.gif" }
    

    应该由返回的最终json字符串

    {
        "photoId": 1000000007,
        "photoType": "image/gif",
        "photoUrl": "https://slack-imgs.com/?c=1&url=https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2F3o84U9arAYRM73AIvu%2Fgiphy-downsized.gif",
        "isActive": true
    }
    

    保存到表的URL

    https://slack-imgs.com/?c=1&url=https%3A%2F%2Fmedia0.giphy.com%2Fmedia%2F3o84U9arAYRM73AIvu%2Fgiphy-downsized.gif
    

    最终这是一个sql server 2016的更改,但我需要一个比微软提供的更快的修复。那么,有没有办法通过sql或c.net代码来处理这个问题呢?

    奇怪的是,当在ssms中单击表列值json时,它会给出同样的错误。

    enter image description here

    2 回复  |  直到 6 年前
        1
  •  1
  •   David Browne - Microsoft    6 年前

    我将继续并建议您使用错误的api来读取for json查询结果。下面是一个实现sqlcommand.executejsonreader()扩展方法的小助手类。

        static class SqlJsonUtils
        {
    
            public static Newtonsoft.Json.JsonReader ExecuteJsonReader(this SqlCommand cmd)
            {
                var rdr = cmd.ExecuteReader();
                var jr = new Newtonsoft.Json.JsonTextReader(new SqlJSONReader(rdr));
                return jr;
    
            }
    
            class SqlJSONReader : System.IO.TextReader
            {
                SqlDataReader rdr;
                string currentLine = "";
                int currentPos = 0;
                public SqlJSONReader(SqlDataReader rdr)
                {
                    this.rdr = rdr;
                }
                public override int Peek()
                {
                    return GetChar(false);
                }
                public override int Read()
                {
                    return GetChar(true);
                }
                public int GetChar(bool Advance)
                {
                    while (currentLine.Length == currentPos)
                    {
                        if (!rdr.Read())
                        {
                            return -1;
                        }
                        currentLine = rdr.GetString(0);
                        currentPos = 0;
                    }
                    int rv = (int)currentLine[currentPos];
                    if (Advance) currentPos += 1;
                    return rv;
                }
    
                public override void Close()
                {
                    rdr.Close();
                }
    
            }
    
    
        }
    
        2
  •  0
  •   ΩmegaMan    6 年前

    从那以后我就把这个做成了一个可以下载的nuget包。见 SQLJSONReader 有异步更新。


    我采纳了大卫的建议,并将其作为答案,但我需要整个原始的json。

    所以我添加了 Newtonsoft.Json.JsonTextReader 因为它不会只返回一个字符串并通过调用 ReadAll .

    注意它使用了newtonsoft的 JsonConvert.DeserializeObject

    代码

    public static SqlJSONReader ExecuteJsonReader(this SqlCommand cmd)
    {
        var rdr = cmd.ExecuteReader();
        return new SqlJSONReader(rdr);
    }
    
    public class SqlJSONReader : System.IO.TextReader
    {
        private SqlDataReader SqlReader   { get; set; }
        private string CurrentLine        { get; set; }
        private int CurrentPostion        { get; set; }
    
        public SqlJSONReader(SqlDataReader rdr)
        {
            CurrentLine = "";
            CurrentPostion = 0;
            this.SqlReader = rdr;
        }
        public override int Peek()
        {
            return GetChar(false);
        }
        public override int Read()
        {
            return GetChar(true);
        }
        public int GetChar(bool Advance)
        {
            while (CurrentLine.Length == CurrentPostion)
            {
                if (!SqlReader.Read())
                {
                    return -1;
                }
                CurrentLine = SqlReader.GetString(0);
                CurrentPostion = 0;
            }
            var rv = CurrentLine[CurrentPostion];
            if (Advance) 
                CurrentPostion += 1;
    
            return rv;
        }
    
        public string ReadAll()
        {
            var sbResult = new StringBuilder();
    
            if (SqlReader.HasRows)
            {
                while (SqlReader.Read())
                    sbResult.Append(SqlReader.GetString(0));
    
            }
            else
                return string.Empty;
    
            // Clean up any JSON escapes before returning
            return  JsonConvert.DeserializeObject(sbResult.ToString()).ToString();
        }
    
        public override void Close() { SqlReader.Close(); }
    }
    

    用法

    using (SqlConnection conn = new SqlConnection(connectionString))
        using (SqlCommand cmd = conn.CreateCommand())
    {
    
        cmd.CommandText = "exec [dbo].[GetPhoto] @PhotoId=4";
        conn.Open();
        var rdr = cmd.ExecuteJsonReader();
    
        string jsonResult = rdr.ReadAll();
    
        conn.Close();
    
    }