diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs
index 70c91bc184..71d94375bb 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlDataReader.cs
@@ -1994,7 +1994,7 @@ override public TextReader GetTextReader(int i)
{
encoding = _metaData[i].encoding;
}
-
+ encoding = mt.SqlDbType == SqlDbTypeExtensions.Json ? System.Text.Encoding.UTF8 : encoding;
_currentTextReader = new SqlSequentialTextReader(this, i, encoding);
_lastColumnWithDataChunkRead = i;
return _currentTextReader;
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
index 02b26a3a1b..b68ad0dcc6 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -11911,7 +11911,7 @@ private async Task WriteTextFeed(TextDataFeed feed, Encoding encoding, bool need
encoding = encoding ?? TextDataFeed.DefaultEncoding;
- using (ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size))
+ using (ConstrainedTextWriter writer = encoding == Encoding.UTF8 ? new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null)), size) : new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size))
{
if (needBom)
{
@@ -12175,7 +12175,8 @@ private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int
}
else
{
- return NullIfCompletedWriteTask(WriteTextFeed(tdf, null, IsBOMNeeded(type, value), stateObj, paramSize));
+ Encoding encoding = type.NullableType == TdsEnums.SQLJSON ? Encoding.UTF8 : null;
+ return NullIfCompletedWriteTask(WriteTextFeed(tdf, encoding, IsBOMNeeded(type, value), stateObj, paramSize));
}
}
else
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
index 0c0b15a330..9d2110181d 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlDataReader.cs
@@ -2283,7 +2283,7 @@ override public TextReader GetTextReader(int i)
{
encoding = _metaData[i].encoding;
}
-
+ encoding = mt.SqlDbType == SqlDbTypeExtensions.Json ? System.Text.Encoding.UTF8 : encoding;
_currentTextReader = new SqlSequentialTextReader(this, i, encoding);
_lastColumnWithDataChunkRead = i;
return _currentTextReader;
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
index 018078a66f..42e4c628a1 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/TdsParser.cs
@@ -12904,7 +12904,7 @@ private async Task WriteTextFeed(TextDataFeed feed, Encoding encoding, bool need
char[] inBuff = new char[constTextBufferSize];
encoding = encoding ?? new UnicodeEncoding(false, false);
- ConstrainedTextWriter writer = new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size);
+ ConstrainedTextWriter writer = encoding == Encoding.UTF8 ? new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null)), size) : new ConstrainedTextWriter(new StreamWriter(new TdsOutputStream(this, stateObj, null), encoding), size);
if (needBom)
{
@@ -13163,7 +13163,8 @@ private Task WriteUnterminatedValue(object value, MetaType type, byte scale, int
}
else
{
- return NullIfCompletedWriteTask(WriteTextFeed(tdf, null, IsBOMNeeded(type, value), stateObj, paramSize));
+ Encoding encoding = type.NullableType == TdsEnums.SQLJSON ? Encoding.UTF8 : null;
+ return NullIfCompletedWriteTask(WriteTextFeed(tdf, encoding, IsBOMNeeded(type, value), stateObj, paramSize));
}
}
else
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
index dafb0c5ef5..758f7fd414 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
@@ -124,6 +124,7 @@ private static bool _IsCharType(SqlDbType type) =>
type == SqlDbType.Char ||
type == SqlDbType.VarChar ||
type == SqlDbType.Text ||
+ type == SqlDbTypeExtensions.Json ||
type == SqlDbType.Xml;
private static bool _IsNCharType(SqlDbType type) =>
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
index febde5acbc..3c12e15db8 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/DataCommon/DataTestUtility.cs
@@ -590,6 +590,17 @@ public static string GetUniqueNameForSqlServer(string prefix, bool withBracket =
return name;
}
+ public static void CreateTable(SqlConnection sqlConnection, string tableName, string createBody)
+ {
+ DropTable(sqlConnection, tableName);
+ string tableCreate = "CREATE TABLE " + tableName + createBody;
+ using (SqlCommand command = sqlConnection.CreateCommand())
+ {
+ command.CommandText = tableCreate;
+ command.ExecuteNonQuery();
+ }
+ }
+
public static void DropTable(SqlConnection sqlConnection, string tableName)
{
ResurrectConnection(sqlConnection);
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
index 25baa19768..30e5e18d96 100644
--- a/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Microsoft.Data.SqlClient.ManualTesting.Tests.csproj
@@ -291,6 +291,7 @@
+
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs
new file mode 100644
index 0000000000..e6c35c9b6e
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/SQL/JsonTest/JsonStreamTest.cs
@@ -0,0 +1,197 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Data;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+using Xunit.Abstractions;
+using Newtonsoft.Json.Linq;
+
+
+namespace Microsoft.Data.SqlClient.ManualTesting.Tests
+{
+ public class JsonRecord
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+
+ public class JsonStreamTest
+ {
+ private readonly ITestOutputHelper _output;
+ private static readonly string _jsonFile = "randomRecords.json";
+ private static readonly string _outputFile = "serverRecords.json";
+
+ public JsonStreamTest(ITestOutputHelper output)
+ {
+ _output = output;
+ }
+
+ private void GenerateJsonFile(int noOfRecords, string filename)
+ {
+ DeleteFile(filename);
+ var random = new Random();
+ var records = new List();
+ int recordCount = noOfRecords;
+
+ for (int i = 0; i < recordCount; i++)
+ {
+ records.Add(new JsonRecord
+ {
+ Id = i + 1,
+ Name = "𩸽json" + random.Next(1, noOfRecords),
+ });
+ }
+
+ string json = JsonConvert.SerializeObject(records, Formatting.Indented);
+ File.WriteAllText(filename, json);
+ Assert.True(File.Exists(filename));
+ _output.WriteLine("Generated JSON file "+filename);
+ }
+
+ private void CompareJsonFiles()
+ {
+ using (var stream1 = File.OpenText(_jsonFile))
+ using (var stream2 = File.OpenText(_outputFile))
+ using (var reader1 = new JsonTextReader(stream1))
+ using (var reader2 = new JsonTextReader(stream2))
+ {
+ var jToken1 = JToken.ReadFrom(reader1);
+ var jToken2 = JToken.ReadFrom(reader2);
+ Assert.True(JToken.DeepEquals(jToken1, jToken2));
+ }
+ }
+
+ private void PrintJsonDataToFile(SqlConnection connection)
+ {
+ DeleteFile(_outputFile);
+ using (SqlCommand command = new SqlCommand("SELECT [data] FROM [jsonTab]", connection))
+ {
+ using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.SequentialAccess))
+ {
+ using (StreamWriter sw = new StreamWriter(_outputFile))
+ {
+ while (reader.Read())
+ {
+ char[] buffer = new char[4096];
+ int charsRead = 0;
+
+ using (TextReader data = reader.GetTextReader(0))
+ {
+ do
+ {
+ charsRead = data.Read(buffer, 0, buffer.Length);
+ sw.Write(buffer, 0, charsRead);
+
+ } while (charsRead > 0);
+ }
+ _output.WriteLine("Output written to " + _outputFile);
+ }
+ }
+ }
+ }
+ }
+
+ private async Task PrintJsonDataToFileAsync(SqlConnection connection)
+ {
+ DeleteFile(_outputFile);
+ using (SqlCommand command = new SqlCommand("SELECT [data] FROM [jsonTab]", connection))
+ {
+ using (SqlDataReader reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
+ {
+ using (StreamWriter sw = new StreamWriter(_outputFile))
+ {
+ while (await reader.ReadAsync())
+ {
+ char[] buffer = new char[4096];
+ int charsRead = 0;
+
+ using (TextReader data = reader.GetTextReader(0))
+ {
+ do
+ {
+ charsRead = await data.ReadAsync(buffer, 0, buffer.Length);
+ await sw.WriteAsync(buffer, 0, charsRead);
+
+ } while (charsRead > 0);
+ }
+ _output.WriteLine("Output written to file " + _outputFile);
+ }
+ }
+ }
+ }
+ }
+
+ private void StreamJsonFileToServer(SqlConnection connection)
+ {
+ using (SqlCommand cmd = new SqlCommand("INSERT INTO [jsonTab] (data) VALUES (@jsondata)", connection))
+ {
+ using (StreamReader jsonFile = File.OpenText(_jsonFile))
+ {
+ cmd.Parameters.Add("@jsondata", Microsoft.Data.SqlDbTypeExtensions.Json, -1).Value = jsonFile;
+ cmd.ExecuteNonQuery();
+ }
+ }
+ }
+
+ private async Task StreamJsonFileToServerAsync(SqlConnection connection)
+ {
+ using (SqlCommand cmd = new SqlCommand("INSERT INTO [jsonTab] (data) VALUES (@jsondata)", connection))
+ {
+ using (StreamReader jsonFile = File.OpenText(_jsonFile))
+ {
+ cmd.Parameters.Add("@jsondata", Microsoft.Data.SqlDbTypeExtensions.Json, -1).Value = jsonFile;
+ await cmd.ExecuteNonQueryAsync();
+ }
+ }
+ }
+
+ private void DeleteFile(string filename)
+ {
+ if (File.Exists(filename))
+ {
+ File.Delete(filename);
+ }
+
+ }
+
+
+ [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))]
+ public void TestJsonStreaming()
+ {
+ GenerateJsonFile(10000, _jsonFile);
+
+ using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString))
+ {
+ connection.Open();
+ DataTestUtility.CreateTable(connection, "jsonTab", "(data json)");
+ StreamJsonFileToServer(connection);
+ PrintJsonDataToFile(connection);
+ CompareJsonFiles();
+ DeleteFile(_jsonFile);
+ DeleteFile(_outputFile);
+ }
+ }
+
+ [ConditionalFact(typeof(DataTestUtility), nameof(DataTestUtility.IsJsonSupported))]
+ public async Task TestJsonStreamingAsync()
+ {
+ GenerateJsonFile(10000, _jsonFile);
+
+ using (SqlConnection connection = new SqlConnection(DataTestUtility.TCPConnectionString))
+ {
+ await connection.OpenAsync();
+ DataTestUtility.CreateTable(connection, "jsonTab", "(data json)");
+ await StreamJsonFileToServerAsync(connection);
+ await PrintJsonDataToFileAsync(connection);
+ CompareJsonFiles();
+ DeleteFile(_jsonFile);
+ DeleteFile(_outputFile);
+ }
+ }
+ }
+}
+