diff --git a/doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml b/doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml
new file mode 100644
index 0000000000..6d55346417
--- /dev/null
+++ b/doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<docs>
+  <members name="SqlJson">
+    <SqlJson>
+      <summary>Represents the JSON datatype in SQL Server.</summary>
+    </SqlJson>
+    <ctor1>
+      <summary>Parameterless constructor. Initializes a new instance of the SqlJson class which represents a null JSON value.</summary>
+    </ctor1>
+    <ctor2>
+      <param name="jsonString"></param>
+      <summary>Takes a <see cref="string"/> as input and initializes a new instance of the SqlJson class.</summary>
+    </ctor2>
+    <ctor3>
+      <param name="jsonDoc"></param>
+      <summary>Takes a <see cref="System.Text.Json.JsonDocument"/> as input and initializes a new instance of the SqlJson class.</summary>
+    </ctor3>
+    <IsNull>
+      <inheritdoc/>
+    </IsNull>
+    <Null>
+      <summary>Represents a null instance of the <see cref="SqlJson"/> type.</summary>
+    </Null>
+    <Value>
+      <summary>Gets the string representation of the Json content of this <see cref="SqlJson" /> instance.</summary>
+    </Value>
+    <GetSqlJson>
+      <param name="i"></param>
+      <summary>Retrieves the column at ordinal as a <see cref="SqlJson"/>.</summary>
+      <returns>A <see cref="SqlJson"/> object representing the column at the given ordinal.</returns>
+    </GetSqlJson>
+  </members>
+</docs>
diff --git a/src/Microsoft.Data.SqlClient.sln b/src/Microsoft.Data.SqlClient.sln
index 88d02dee48..b203aa0795 100644
--- a/src/Microsoft.Data.SqlClient.sln
+++ b/src/Microsoft.Data.SqlClient.sln
@@ -177,6 +177,7 @@ EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Microsoft.Data.SqlTypes", "Microsoft.Data.SqlTypes", "{5A7600BD-AED8-44AB-8F2A-7CB33A8D9C02}"
 	ProjectSection(SolutionItems) = preProject
 		..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlFileStream.xml
+		..\doc\snippets\Microsoft.Data.SqlTypes\SqlJson.xml = ..\doc\snippets\Microsoft.Data.SqlTypes\SqlJson.xml
 	EndProjectSection
 EndProject
 Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{F5DF2FDC-C860-4CB3-8B24-7C903C6FC076}"
diff --git a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
index 66a86009af..b234cdc3dd 100644
--- a/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
+++ b/src/Microsoft.Data.SqlClient/netcore/ref/Microsoft.Data.SqlClient.cs
@@ -100,6 +100,25 @@ public override void EndWrite(System.IAsyncResult asyncResult) { }
         /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml' path='docs/members[@name="SqlFileStream"]/WriteByte/*' />
         public override void WriteByte(byte value) { }
     }
+
+    /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/SqlJson/*' />
+    public class SqlJson : System.Data.SqlTypes.INullable
+    {
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor1/*' />
+        public SqlJson() { }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor2/*' />
+        public SqlJson(string jsonString) { }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor3/*' />
+        public SqlJson(System.Text.Json.JsonDocument jsonDoc) { }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/IsNull/*' />
+        public bool IsNull => throw null; 
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Null/*' />
+        public static SqlJson Null => throw null;
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Value/*' />
+        public string Value { get { throw null; } }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/GetSqlJson/*' />
+        virtual public SqlJson GetSqlJson(int i) { throw null; }
+    }
 }
 namespace Microsoft.Data.SqlClient
 {
diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
index 3bc178e7ae..594a4beb20 100644
--- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj
@@ -621,6 +621,9 @@
     <Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs">
       <Link>Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs</Link>
     </Compile>
+    <Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlJson.cs">
+      <Link>Microsoft\Data\SqlTypes\SqlJson.cs</Link>
+    </Compile>
     <Compile Include="$(CommonSourceRoot)Resources\ResCategoryAttribute.cs">
       <Link>Resources\ResCategoryAttribute.cs</Link>
     </Compile>
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 35a8bacfe2..fd53e3fe50 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
@@ -15,6 +15,7 @@
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
@@ -2545,6 +2546,14 @@ virtual public SqlXml GetSqlXml(int i)
             return sx;
         }
 
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/GetSqlJson/*' />
+        virtual public SqlJson GetSqlJson(int i)
+        {
+            ReadColumn(i);
+            SqlJson json = _data[i].IsNull ? SqlJson.Null : _data[i].SqlJson;
+            return json;
+        }
+
         /// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/GetSqlValue/*' />
         virtual public object GetSqlValue(int i)
         {
@@ -2998,6 +3007,16 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
                     return (T)(object)new MemoryStream(value, writable: false);
                 }
             }
+            else if (typeof(T) == typeof(JsonDocument))
+            {
+                MetaType metaType = metaData.metaType;
+                if (metaType.SqlDbType != SqlDbTypeExtensions.Json)
+                {
+                    throw SQL.JsonDocumentNotSupportedOnColumnType(metaData.column);
+                }
+                JsonDocument document = JsonDocument.Parse(data.Value as string);
+                return (T)(object)document;
+            }
             else
             {
                 if (typeof(INullable).IsAssignableFrom(typeof(T)))
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 f0811f268c..cbd4e2142e 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
@@ -5952,7 +5952,6 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
                 case TdsEnums.SQLVARCHAR:
                 case TdsEnums.SQLBIGVARCHAR:
                 case TdsEnums.SQLTEXT:
-                case TdsEnums.SQLJSON:
                     // If bigvarchar(max), we only read the first chunk here,
                     // expecting the caller to read the rest
                     if (encoding == null)
@@ -5970,6 +5969,17 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
                     value.SetToString(stringValue);
                     break;
 
+                case TdsEnums.SQLJSON:
+                    encoding = Encoding.UTF8;
+                    string jsonStringValue;
+                    result = stateObj.TryReadStringWithEncoding(length, encoding, isPlp, out jsonStringValue);
+                    if (result != TdsOperationStatus.Done)
+                    {
+                        return result;
+                    }
+                    value.SetToJson(jsonStringValue);
+                    break;
+
                 case TdsEnums.SQLNCHAR:
                 case TdsEnums.SQLNVARCHAR:
                 case TdsEnums.SQLNTEXT:
@@ -9616,7 +9626,8 @@ private Task TDSExecuteRPCAddParameter(TdsParserStateObject stateObj, SqlParamet
                     mt.TDSType != TdsEnums.SQLXMLTYPE &&
                     mt.TDSType != TdsEnums.SQLIMAGE &&
                     mt.TDSType != TdsEnums.SQLTEXT &&
-                    mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");
+                    mt.TDSType != TdsEnums.SQLNTEXT &&
+                    mt.TDSType != TdsEnums.SQLJSON, "Type unsupported for encryption");
 
                 byte[] serializedValue = null;
                 byte[] encryptedValue = null;
diff --git a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
index bdcaf82d28..8e64378330 100644
--- a/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
+++ b/src/Microsoft.Data.SqlClient/netfx/ref/Microsoft.Data.SqlClient.cs
@@ -2400,4 +2400,23 @@ public override void EndWrite(System.IAsyncResult asyncResult) { }
         /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlFileStream.xml' path='docs/members[@name="SqlFileStream"]/WriteByte/*' />
         public override void WriteByte(byte value) { }
     }
+
+    /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/SqlJson/*' />
+    public class SqlJson : System.Data.SqlTypes.INullable
+    {
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor1/*' />
+        public SqlJson() { }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor2/*' />
+        public SqlJson(string jsonString) { }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor3/*' />
+        public SqlJson(System.Text.Json.JsonDocument jsonDoc) { }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/IsNull/*' />
+        public bool IsNull => throw null; 
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Null/*' />
+        public static SqlJson Null => throw null;
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Value/*' />
+        public string Value { get { throw null; } }
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/GetSqlJson/*' />
+        virtual public SqlJson GetSqlJson(int i) { throw null; }
+    }
 }
diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
index 3e0139f31b..e0e9b1a903 100644
--- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
+++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj
@@ -651,6 +651,9 @@
     <Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs">
       <Link>Microsoft\Data\SqlTypes\SqlTypeWorkarounds.cs</Link>
     </Compile>
+    <Compile Include="$(CommonSourceRoot)Microsoft\Data\SqlTypes\SqlJson.cs">
+      <Link>Microsoft\Data\SqlTypes\SqlJson.cs</Link>
+    </Compile>
     <Compile Include="$(CommonSourceRoot)Resources\ResCategoryAttribute.cs">
       <Link>Resources\ResCategoryAttribute.cs</Link>
     </Compile>
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 e1bae15b5c..6de0a02f25 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
@@ -14,6 +14,7 @@
 using System.Reflection;
 using System.Runtime.CompilerServices;
 using System.Text;
+using System.Text.Json;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
@@ -2898,6 +2899,15 @@ virtual public SqlXml GetSqlXml(int i)
             return sx;
         }
 
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/GetSqlJson/*' />
+        virtual public SqlJson GetSqlJson(int i)
+        {
+            ReadColumn(i);
+            SqlJson json = _data[i].IsNull ? SqlJson.Null : _data[i].SqlJson;
+
+            return json;
+        }
+
         /// <include file='../../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlDataReader.xml' path='docs/members[@name="SqlDataReader"]/GetSqlValue/*' />
         virtual public object GetSqlValue(int i)
         {
@@ -3345,6 +3355,16 @@ private T GetFieldValueFromSqlBufferInternal<T>(SqlBuffer data, _SqlMetaData met
                     return (T)(object)new MemoryStream(value, writable: false);
                 }
             }
+            else if (typeof(T) == typeof(JsonDocument))
+            {
+                MetaType metaType = metaData.metaType;
+                if (metaType.SqlDbType != SqlDbTypeExtensions.Json)
+                {
+                    throw SQL.JsonDocumentNotSupportedOnColumnType(metaData.column);
+                }
+                JsonDocument document = JsonDocument.Parse(data.Value as string);
+                return (T)(object)document;
+            }
             else
             {
                 if (typeof(INullable).IsAssignableFrom(typeof(T)))
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 c98eeb8db4..c7b7d57171 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
@@ -6801,7 +6801,6 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
                 case TdsEnums.SQLVARCHAR:
                 case TdsEnums.SQLBIGVARCHAR:
                 case TdsEnums.SQLTEXT:
-                case TdsEnums.SQLJSON:
                     // If bigvarchar(max), we only read the first chunk here,
                     // expecting the caller to read the rest
                     if (encoding == null)
@@ -6818,6 +6817,16 @@ private TdsOperationStatus TryReadSqlStringValue(SqlBuffer value, byte type, int
                     }
                     value.SetToString(stringValue);
                     break;
+                case TdsEnums.SQLJSON:
+                    encoding = Encoding.UTF8;
+                    string jsonStringValue;
+                    result = stateObj.TryReadStringWithEncoding(length, encoding, isPlp, out jsonStringValue);
+                    if (result != TdsOperationStatus.Done)
+                    {
+                        return result;
+                    }
+                    value.SetToJson(jsonStringValue);
+                    break;
 
                 case TdsEnums.SQLNCHAR:
                 case TdsEnums.SQLNVARCHAR:
@@ -10370,7 +10379,8 @@ internal Task TdsExecuteRPC(SqlCommand cmd, IList<_SqlRPC> rpcArray, int timeout
                                     mt.TDSType != TdsEnums.SQLXMLTYPE &&
                                     mt.TDSType != TdsEnums.SQLIMAGE &&
                                     mt.TDSType != TdsEnums.SQLTEXT &&
-                                    mt.TDSType != TdsEnums.SQLNTEXT, "Type unsupported for encryption");
+                                    mt.TDSType != TdsEnums.SQLNTEXT &&
+                                    mt.TDSType != TdsEnums.SQLJSON, "Type unsupported for encryption");
 
                                 byte[] serializedValue = null;
                                 byte[] encryptedValue = null;
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
index 6ed6b68157..0d37d5eaf7 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlBuffer.cs
@@ -36,6 +36,7 @@ internal enum StorageType
             DateTime2,
             DateTimeOffset,
             Time,
+            Json,
         }
 
         internal struct DateTimeInfo
@@ -486,7 +487,7 @@ internal string String
             {
                 ThrowIfNull();
 
-                if (StorageType.String == _type)
+                if (StorageType.String == _type || StorageType.Json == _type)
                 {
                     return (string)_object;
                 }
@@ -916,7 +917,8 @@ internal SqlString SqlString
         {
             get
             {
-                if (StorageType.String == _type)
+                // String and Json storage type are both strings.
+                if (StorageType.String == _type || StorageType.Json == _type)
                 {
                     if (IsNull)
                     {
@@ -937,6 +939,8 @@ internal SqlString SqlString
             }
         }
 
+        internal SqlJson SqlJson => (StorageType.Json == _type) ? (IsNull ? SqlTypes.SqlJson.Null : new SqlJson((string)_object)) : (SqlJson)SqlValue;
+
         internal object SqlValue
         {
             get
@@ -969,7 +973,8 @@ internal object SqlValue
                         return SqlSingle;
                     case StorageType.String:
                         return SqlString;
-
+                    case StorageType.Json:
+                        return SqlJson;
                     case StorageType.SqlCachedBuffer:
                         {
                             SqlCachedBuffer data = (SqlCachedBuffer)(_object);
@@ -1087,6 +1092,8 @@ internal object Value
                         return DateTimeOffset;
                     case StorageType.Time:
                         return Time;
+                    case StorageType.Json:
+                        return String;
                 }
                 return null; // need to return the value as an object of some CLS type
             }
@@ -1132,6 +1139,8 @@ internal Type GetTypeFromStorageType(bool isSqlType)
                         return typeof(SqlGuid);
                     case StorageType.SqlXml:
                         return typeof(SqlXml);
+                    case StorageType.Json:
+                        return typeof(SqlJson);
                         // Time Date DateTime2 and DateTimeOffset have no direct Sql type to contain them
                 }
             }
@@ -1179,6 +1188,8 @@ internal Type GetTypeFromStorageType(bool isSqlType)
                         return typeof(DateTime);
                     case StorageType.DateTimeOffset:
                         return typeof(DateTimeOffset);
+                    case StorageType.Json:
+                        return typeof(string);
 #if NET6_0_OR_GREATER
                     case StorageType.Time:
                         return typeof(TimeOnly);
@@ -1274,6 +1285,14 @@ internal void SetToString(string value)
             _isNull = false;
         }
 
+        internal void SetToJson(string value)
+        {
+            Debug.Assert(IsEmpty, "setting value a second time?");
+            _object = value;
+            _type = StorageType.Json;
+            _isNull = false;
+        }
+
         internal void SetToDate(ReadOnlySpan<byte> bytes)
         {
             Debug.Assert(IsEmpty, "setting value a second time?");
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 318df52a0d..a12b274297 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlEnums.cs
@@ -16,6 +16,7 @@
 using System.IO;
 using System.Xml;
 using Microsoft.Data.Common;
+using Microsoft.Data.SqlTypes;
 using Microsoft.SqlServer.Server;
 
 namespace Microsoft.Data.SqlClient
@@ -365,6 +366,8 @@ private static MetaType GetMetaTypeFromValue(Type dataType, object value, bool i
                         return s_metaReal;
                     else if (dataType == typeof(SqlXml))
                         return MetaXml;
+                    else if (dataType == typeof(SqlJson))
+                        return s_MetaJson;
                     else if (dataType == typeof(SqlString))
                     {
                         return ((inferLen && !((SqlString)value).IsNull)
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs
index da214059a0..0d88520622 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlParameter.cs
@@ -18,6 +18,7 @@
 using System.Xml;
 using Microsoft.Data.Common;
 using Microsoft.Data.SqlClient.Server;
+using Microsoft.Data.SqlTypes;
 
 namespace Microsoft.Data.SqlClient
 {
@@ -2247,6 +2248,10 @@ internal static object CoerceValue(object value, MetaType destinationType, out b
                         {
                             value = MetaType.GetStringFromXml((XmlReader)(((SqlXml)value).CreateReader()));
                         }
+                        else if (currentType == typeof(SqlJson))
+                        {
+                            value = (value as SqlJson).Value;
+                        }
                         else if (currentType == typeof(SqlString))
                         {
                             typeChanged = false;   // Do nothing
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs
index 8592fd7407..a035475426 100644
--- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlUtil.cs
@@ -987,6 +987,11 @@ internal static Exception StreamNotSupportOnColumnType(string columnName)
             return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_StreamNotSupportOnColumnType, columnName));
         }
 
+        internal static Exception JsonDocumentNotSupportedOnColumnType(string columnName)
+        {
+            return ADP.InvalidCast(StringsHelper.GetString(Strings.SQL_JsonDocumentNotSupportedOnColumnType, columnName));
+        }
+
         internal static Exception StreamNotSupportOnEncryptedColumn(string columnName)
         {
             return ADP.InvalidOperation(StringsHelper.GetString(Strings.TCE_StreamNotSupportOnEncryptedColumn, columnName, "Stream"));
diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs
new file mode 100644
index 0000000000..50841d3a68
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlTypes/SqlJson.cs
@@ -0,0 +1,103 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+using System.Data.SqlTypes;
+using System.Text;
+using System.Text.Json;
+
+#nullable enable
+
+namespace Microsoft.Data.SqlTypes
+{
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/SqlJson/*' />
+    public class SqlJson : INullable
+    {
+
+        /// <summary>
+        /// True if null.
+        /// </summary>
+        private bool _isNull;         
+        
+        private readonly string? _jsonString;
+
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor1/*' />
+        public SqlJson()
+        {
+            SetNull();
+        }
+
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor2/*' />
+        public SqlJson(string? jsonString) 
+        {
+            if (jsonString == null)
+            {
+                SetNull();
+            }
+            else
+            {
+                // TODO: We need to validate the Json before storing it.
+                ValidateJson(jsonString);
+                _jsonString = jsonString;
+            }
+        }
+
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/ctor3/*' />
+        public SqlJson(JsonDocument? jsonDoc) 
+        {
+            if (jsonDoc == null)
+            {
+                SetNull();
+            }
+            else
+            {
+                _jsonString = jsonDoc.RootElement.GetRawText();
+            }
+        }
+
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/IsNull/*' />
+        public bool IsNull => _isNull;
+
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Null/*' />
+        public static SqlJson Null => new();
+
+        /// <include file='../../../../doc/snippets/Microsoft.Data.SqlTypes/SqlJson.xml' path='docs/members[@name="SqlJson"]/Value/*' />
+        public string Value 
+        { 
+            get
+            {
+                if (IsNull)
+                {
+                    throw new SqlNullValueException();
+                }
+                else
+                {
+                    return _jsonString!;
+                }
+            }
+        }
+
+        private void SetNull()
+        {
+            _isNull = true;
+        }
+
+        private static void ValidateJson(string jsonString)
+        {
+            // Convert the JSON string to a UTF-8 byte array
+            byte[] jsonBytes = Encoding.UTF8.GetBytes(jsonString);
+
+            // Create a Utf8JsonReader instance
+            var reader = new Utf8JsonReader(jsonBytes, isFinalBlock: true, state: default);
+
+            // Read through the JSON data
+            while (reader.Read())
+            {
+                // The Read method advances the reader to the next token
+                // If the JSON is invalid, an exception will be thrown
+            }
+            // If we reach here, the JSON is valid
+        }
+    }
+}
diff --git a/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs b/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs
index a685787e9e..8302a288fe 100644
--- a/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs
+++ b/src/Microsoft.Data.SqlClient/src/Resources/Strings.Designer.cs
@@ -10484,7 +10484,16 @@ internal static string SQL_StreamNotSupportOnColumnType {
                 return ResourceManager.GetString("SQL_StreamNotSupportOnColumnType", resourceCulture);
             }
         }
-        
+
+        /// <summary>
+        ///    Looks up a localized string similar to Invalid attempt to get JsonDocument on column &apos;{0}&apos;. JsonDocument is only supported for columns of type json.
+        /// </summary>
+        internal static string SQL_JsonDocumentNotSupportedOnColumnType {
+            get {
+                return ResourceManager.GetString("SQL_JsonDocumentNotSupportedOnColumnType", resourceCulture);
+            }
+        }
+
         /// <summary>
         ///   Looks up a localized string similar to The Stream does not support reading..
         /// </summary>
diff --git a/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx b/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
index c2dd68b867..facdb0ed04 100644
--- a/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
+++ b/src/Microsoft.Data.SqlClient/src/Resources/Strings.resx
@@ -4740,4 +4740,7 @@
   <data name="SQL_RemoteCertificateDoesNotMatchServerCertificate" xml:space="preserve">
     <value>The certificate provided by the server does not match the certificate provided by the ServerCertificate option.</value>
   </data>
+  <data name="SQL_JsonDocumentNotSupportedOnColumnType" xml:space="preserve">
+    <value>Invalid attempt to get JsonDocument on column '{0}'. JsonDocument is only supported for columns of type json.</value>
+  </data>
 </root>
diff --git a/src/Microsoft.Data.SqlClient/tests/ManualTests/Json/SqlJsonTest.cs b/src/Microsoft.Data.SqlClient/tests/ManualTests/Json/SqlJsonTest.cs
new file mode 100644
index 0000000000..03ead359f0
--- /dev/null
+++ b/src/Microsoft.Data.SqlClient/tests/ManualTests/Json/SqlJsonTest.cs
@@ -0,0 +1,151 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Data.SqlTypes;
+using System.Linq;
+using System.Text.Json;
+using Microsoft.Data.SqlTypes;
+using Xunit;
+
+namespace Microsoft.Data.SqlClient.ManualTesting.Tests.Json
+{
+
+    public class SqlJsonTest
+    {
+        [Fact]
+        public void SqlJsonTest_Null()
+        {
+            SqlJson json = new();
+            Assert.True(json.IsNull);
+            Assert.Throws<SqlNullValueException>(() => json.Value);
+
+        }
+
+        [Fact]
+        public void SqlJsonTest_NullString()
+        {
+            string nullString = null;
+            SqlJson json = new(nullString);
+            Assert.True(json.IsNull);
+            Assert.Throws<SqlNullValueException>(() => json.Value);
+        }
+
+        [Fact]
+        public void SqlJsonTest_NullJsonDocument()
+        {
+            JsonDocument doc = null;
+            SqlJson json = new(doc);
+            Assert.True(json.IsNull);
+            Assert.Throws<SqlNullValueException>(() => json.Value);
+        }
+
+        [Fact]
+        public void SqlJsonTest_String()
+        {
+            SqlJson json = new("{\"key\":\"value\"}");
+            Assert.False(json.IsNull);
+            Assert.Equal("{\"key\":\"value\"}", json.Value);
+        }
+
+        [Fact]
+        public void SqlJsonTest_BadString()
+        {
+            Assert.ThrowsAny<JsonException>(()=> new SqlJson("{\"key\":\"value\""));           
+        }
+
+        [Fact]
+        public void SqlJsonTest_JsonDocument()
+        {
+            JsonDocument doc = GenerateRandomJson();
+            SqlJson json = new(doc);
+            Assert.False(json.IsNull);
+
+            var outputDocument = JsonDocument.Parse(json.Value);
+            Assert.True(JsonElementsAreEqual(doc.RootElement, outputDocument.RootElement));
+        }
+
+        [Fact]
+        public void SqlJsonTest_NullProperty()
+        {
+            SqlJson json = SqlJson.Null;
+            Assert.True(json.IsNull);
+            Assert.Throws<SqlNullValueException>(() => json.Value);
+        }
+
+        static JsonDocument GenerateRandomJson()
+        {
+            var random = new Random();
+
+            var jsonObject = new
+            {
+                id = random.Next(1, 1000),
+                name = $"Name{random.Next(1, 100)}",
+                isActive = random.Next(0, 2) == 1,
+                createdDate = DateTime.Now.AddDays(-random.Next(1, 100)).ToString("yyyy-MM-ddTHH:mm:ssZ"),
+                scores = new int[] { random.Next(1, 100), random.Next(1, 100), random.Next(1, 100) },
+                details = new
+                {
+                    age = random.Next(18, 60),
+                    city = $"City{random.Next(1, 100)}"
+                }
+            };
+
+            string jsonString = JsonSerializer.Serialize(jsonObject, new JsonSerializerOptions { WriteIndented = true });
+            return JsonDocument.Parse(jsonString);
+        }
+
+        static bool JsonElementsAreEqual(JsonElement element1, JsonElement element2)
+        {
+            if (element1.ValueKind != element2.ValueKind)
+                return false;
+
+            switch (element1.ValueKind)
+            {
+                case JsonValueKind.Object:
+                    {
+                        JsonElement.ObjectEnumerator obj1 = element1.EnumerateObject();
+                        JsonElement.ObjectEnumerator obj2 = element2.EnumerateObject();
+                        var dict1 = obj1.ToDictionary(p => p.Name, p => p.Value);
+                        var dict2 = obj2.ToDictionary(p => p.Name, p => p.Value);
+
+                        if (dict1.Count != dict2.Count)
+                            return false;
+
+                        foreach (var kvp in dict1)
+                        {
+                            if (!dict2.TryGetValue(kvp.Key, out var value2))
+                                return false;
+
+                            if (!JsonElementsAreEqual(kvp.Value, value2))
+                                return false;
+                        }
+
+                        return true;
+                    }
+                case JsonValueKind.Array:
+                    {
+                        var array1 = element1.EnumerateArray();
+                        var array2 = element2.EnumerateArray();
+
+                        if (array1.Count() != array2.Count())
+                            return false;
+
+                        return array1.Zip(array2, (e1, e2) => JsonElementsAreEqual(e1, e2)).All(equal => equal);
+                    }
+                case JsonValueKind.String:
+                    return element1.GetString() == element2.GetString();
+                case JsonValueKind.Number:
+                    return element1.GetDecimal() == element2.GetDecimal();
+                case JsonValueKind.True:
+                case JsonValueKind.False:
+                    return element1.GetBoolean() == element2.GetBoolean();
+                case JsonValueKind.Null:
+                    return true;
+                default:
+                    throw new NotSupportedException($"Unsupported JsonValueKind: {element1.ValueKind}");
+            }
+        }
+    }
+}
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 8bcd2dfd16..35a6b80fcd 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
@@ -280,6 +280,7 @@
     <Compile Include="DataCommon\ProxyServer.cs" />
     <Compile Include="DataCommon\SqlClientCustomTokenCredential.cs" />
     <Compile Include="DataCommon\SystemDataResourceManager.cs" />
+    <Compile Include="Json\SqlJsonTest.cs" />
     <Compile Include="SQL\Common\AsyncDebugScope.cs" />
     <Compile Include="SQL\Common\ConnectionPoolWrapper.cs" />
     <Compile Include="SQL\Common\InternalConnectionWrapper.cs" />