I am using AZURE SQL (SQL Server 2016) and creating a query to give me output in JSON object. I am adding FOR JSON PATH
at the end of query.
When I exe
When FOR JSON queries are returned to the client, the JSON text is returned as a single-column result set. The JSON is broken into fixed-length strings and sent over multiple rows.
It's really hard to see this properly in SSMS, as SSMS concatenates the results for you in "Results to Grid", and truncates each row in "Results to Text".
Why? Dunno. My guess is that only .NET clients know how to efficiently read large streams from SQL Server, and 99% of the time users will still just buffer the whole object. Breaking the JSON over multiple rows gives clients a simple API to read the data incrementally. And in .NET the fact that the de facto standard JSON library is not in the BCL means that SqlClient can't really have a first-class JSON API.
Anyway, from C#, you can use something like this to read the results:
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class SqlJSONReader: 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();
}
}
class Program
{
static void Main(string[] args)
{
using (var con = new SqlConnection("server=.;database=master;Integrated Security=true"))
{
con.Open();
var sql = @"
select o.object_id as [obj.Id], replicate('n', 2000) as [obj.foo], c.name as [obj.col.name]
from sys.objects o
join sys.columns c
on c.object_id = o.object_id
for json path;
"
;
var cmd = new SqlCommand(sql, con);
var sr = new StringBuilder();
using (var rdr = cmd.ExecuteReader())
{
using (var tr = new SqlJSONReader(rdr))
{
using (var jr = new Newtonsoft.Json.JsonTextReader(tr))
{
while (jr.Read())
{
Console.WriteLine($" {jr.TokenType} : {jr.Value}");
}
}
}
}
Console.WriteLine(sr.ToString());
}
}
}
}
If your query returned more then 2033 charters then there will be rows. Each row contains 2033 charters data another row contains remaining data. So you need to merge to get actual json. As seen below code sample.
dynamic jsonReturned = unitOfWork
.Database
.FetchProc<string>("storedProcedureGetSaleData", new { ProductId = productId });
if (Enumerable.Count(jsonReturned) == 0)
{
return null;
}
dynamic combinedJson = "";
foreach (var resultJsonRow in jsonReturned)
{
combinedJson += resultJsonRow;
}
return combinedJsonResult;
Separation of Concerns dictates returning a string and parsing the JSON separately. The below snippet can be used with no dependency on JSON.net which can be be used separately or a different Json Deserializer can be used (e.g. the one built into RestSharp) and does not require the SqlJSONReader class.
try {
using (var conn = new SqlConnection(connectionString))
using (var cmd = new SqlCommand(sql, conn)) {
await conn.OpenAsync();
logger.LogInformation("SQL:" + sql);
var rdr = await cmd.ExecuteReaderAsync().ConfigureAwait(false);
var result = "";
var moreRows = rdr.HasRows;
while (moreRows) {
moreRows = await rdr.ReadAsync();
if (moreRows) result += rdr.GetString(0);
}
return result;
}
}
catch (Exception ex) {
//logger.LogError($"Error accessing Db:{ex}");
return null;
}
sql Query result of for json path is a long string that is divided to multi column or block something like this statement: "i want to get result of for json path"
"i want"+
" to ge"+
"t resu"+
"lt of "+
"for js"+
"on path"
each block is equal to max size of column in sql so just get them all to a list
public IHttpActionResult GetAdvertises()
{
var rEQUEST = db.Database.SqlQuery<string>("SELECT
ID,CITY_NAME,JSON_QUERY(ALBUM) AS ALBUM FOR JSON PATH").ToList();
foreach(string req in rEQUEST)
{
HttpContext.Current.Response.Write(req);
}
return Ok();
}
With thanks to @David Browne. I found I had to use 'print' instead of 'select'
declare @json varchar(max) = (SELECT * FROM dbo.AppSettings FOR JSON AUTO)
print @json