I have a webform consisting of input and select fields, additionally there is a set of dynamic fields that range from 3 to n in number. I used JavaScript to transform the form inputs/selects into a JSON object. What I am trying to do is send that JSON object to a SQL stored procedure. What would the most direct way to pass the JSON object from JavaScript to the SQL Server (Microsoft SQL Server Web Edition (64-bit) 10.50.6560.0)? Using APS.net and C#
//FOR SUBMITTING THE FORM
$("#submitRecipe").click(function (e) {
e.preventDefault();
//VARIABLES DEFINED
var IngredientListQuanity;
var MeasurementSizeName;
var IngredientName;
//DEFINE JSON OBJECT FOR FORM DATA
var json = {
"recipe": [{
"RecipeContributor": $("#RecipeContributor").val(),
"RecipeDifficulty": $("#RecipeDifficulty").val(),
"RecipeRating": $("#RecipeRating").val(),
"RecipeInstructions": $("#RecipeInstructions").val(),
"RecipeName": $("#RecipeName").val(),
"RecipePrepTime": $("#RecipePrepTime").val(),
"RecipeCookTime": $("#RecipeCookTime").val(),
"ImageURL": $("#ImageURL").val(),
"RecipeProtein": $("#RecipeProtein").val(),
"RecipeFats": $("#RecipeFats").val(),
"RecipeCarbs": $("#RecipeCarbs").val(),
"RecipeFiber": $("#RecipeFiber").val(),
"RecipeDescription": $("#RecipeDescription").val()
}],
"recipeIngredients": []
};
try {
//FINDS ALL INPUTS WITHIN EACH ROW OF INGREDIENTS
$('#ingredientList').find('.rowHeader').each(function () {
$(this).find('input,select').each(function () {
if ($(this).hasClass("amount")) {
IngredientListQuanity = $(this).val();
} else if ($(this).hasClass("size")) {
MeasurementSizeName = $(this).val();
} else if ($(this).hasClass("ingredient")) {
IngredientName = $(this).val();
} else { console.log($(this).attr('class')) }
});
//APPEND INGREDIENT ROW TO JSON OBJECT
json.recipeIngredients.push({
"IngredientListQuanity": IngredientListQuanity,
"MeasurementSizeName": MeasurementSizeName,
"IngredientName": IngredientName
});
});
$.ajax({
url: '/inputRecipe.aspx/Test',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify(json),
dataType: 'json',
success: function (response) {
//$("#RecipeContributor").val(response.d);
//location.reload();
$("#Status").html(successStory("test"));
$(".alert").fadeOut(5000, function () { $(this).remove(); });
$("#ingredientList").html("");
$("#recipeAdd").find("input, textarea, select").val("");
inputIngredientField();
$("#error").html(JSON.stringify(json, undefined, 2));
},
error: function (xhr, ajaxOptions, thrownError) {
//CLEAR STATUS
$("#Status").html("");
//On error do this
$("#Status").html(errorStory(ajaxOptions + " " + xhr.status + " " + thrownError));
$("#error").html(JSON.stringify(json, undefined, 2));
}
});
} catch (err) {
document.getElementById("Status").innerHTML = err.name;
} finally {
// document.getElementById("Status").innerHTML = "";
}
});
function successStory(successMsg) {
var successRes = "<div class=\"alert alert-success\" role=\"alert\" >" + successMsg + "</div>"
return successRes;
};
function errorStory(errorMsg) {
var errorRes = "<div class=\"alert alert-danger\" role=\"alert\" >" + errorMsg + "</div>"
return errorRes;
};
//FUNCTION THAT POPULATES THE INGREDIENT ROW
function inputIngredientField() {
var inputFields = "<div class=\"form-row rowHeader\">";
inputFields = inputFields + "<div class=\"col\">"
inputFields = inputFields + "<input type=\"text\" class=\"amount form-control\" placeholder=\"Amount\" />";
inputFields = inputFields + "</div>";
inputFields = inputFields + "<div class=\"col\">";
inputFields = inputFields + "<div class=\"form-group\">";
inputFields = inputFields + "<select class=\"size form-control\">";
inputFields = inputFields + "<option>tsp</option>";
inputFields = inputFields + "<option>tbsp</option>";
inputFields = inputFields + "<option>oz</option>";
inputFields = inputFields + "<option>cup</option>";
inputFields = inputFields + "<option>pint</option>";
inputFields = inputFields + "<option>quart</option>";
inputFields = inputFields + "<option>gallon</option>";
inputFields = inputFields + "</select>";
inputFields = inputFields + "</div>";
inputFields = inputFields + "</div>";
inputFields = inputFields + "<div class=\"col\">";
inputFields = inputFields + "<div class=\"form-group\">";
inputFields = inputFields + "<select class=\"ingredient form-control\">";
inputFields = inputFields + "<option>From DB</option>";
inputFields = inputFields + "</select>";
inputFields = inputFields + "</div>";
inputFields = inputFields + "</div>";
inputFields = inputFields + "<div class=\"form-row\">";
inputFields = inputFields + "<div class=\"col form-group pull-right\">";
inputFields = inputFields + "<button class=\"btn btn-primary deleteIngredient\"><span class=\"pr-1 fas fa-trash\"></span></button>";
inputFields = inputFields + "</div>";
inputFields = inputFields + "</div>";
inputFields = inputFields + "</div>";
$("#ingredientList").append(inputFields);
}
});
public class Rootobject
{
public Recipe[] recipe { get; set; }
public Recipeingredient[] recipeIngredients { get; set; }
}
public class Recipe
{
public string RecipeContributor { get; set; }
public string RecipeDifficulty { get; set; }
public string RecipeRating { get; set; }
public string RecipeInstructions { get; set; }
public string RecipeName { get; set; }
public string RecipePrepTime { get; set; }
public string RecipeCookTime { get; set; }
public string ImageURL { get; set; }
public string RecipeProtein { get; set; }
public string RecipeFats { get; set; }
public string RecipeCarbs { get; set; }
public string RecipeFiber { get; set; }
public string RecipeDescription { get; set; }
}
public class Recipeingredient
{
public string IngredientListQuanity { get; set; }
public string MeasurementSizeName { get; set; }
public string IngredientName { get; set; }
}
Until unless you are using SQL-Server 2016
you don't have a native support for the JSON
in MSSQL
.
So, the best approach would be to convert the data in XML
and then pass it to Stored Procedure
, this is most commonly used where applications have to pass a large amount of data to the database.
Method 1 passing data as xml
in order to pass the data, you need to make a few changes to your class definition:
using System.Xml.Serialization;
[XmlRoot(ElementName = "Rootobject")] //defining a root element for the xml
public class Rootobject
{
[XmlElement(ElementName = "recipe")] //defining the name for the serialization
public Recipe[] recipe { get; set; }
[XmlElement(ElementName = "recipeIngredients")]//defining the name for the serialization
public Recipeingredient[] recipeIngredients { get; set; }
}
In the above code, I have made a few changes, I have added an XMLRoot
to specify the root element for the XML and the XmlElement
basically to define a name for the child elements in the root element.
And now in order to send data from javascript
( client-side ) to c#
( server-side ) in asp.net we have to create a static
method which will be attributed by WebMethod ( in simple terms it is a special method which can be called from the javascript or jquery ), Which is as follows:
[WebMethod]
public static string postRootObject(Rootobject roots)
{
try
{
var objectXML = serializeListtoXML<Rootobject>(roots); //converting the given object into an xml string
//passing the data to stored procedure as
var statusSP = sendXMLToSqlServer("readDataFromXML", objectXML);
return "yaaay it works";
}
catch (Exception ex)
{
throw ex;
}
}
And then in the method sendXMLToSqlServer(<procedurename>,<xmldata>)
I am passing the generated xml string to the procedure as:
public static bool sendXMLToSqlServer(string procedure,string xmlData)
{
var status = false;
try
{
using (SqlConnection con = new SqlConnection(@"<your connection string goes here>"))
{
con.Open();
var com = new SqlCommand();
com.CommandText = procedure;
com.Connection = con;
com.Parameters.Add(new SqlParameter("@data",xmlData));
com.CommandType = System.Data.CommandType.StoredProcedure;
//i am using the dataAdapter approach to get the data from the procedure you can write your own code to read the output from the procedure
var ds = new DataSet();
var da = new SqlDataAdapter(com);
da.Fill(ds);
if (ds.Tables[0].Rows.Count > 0) //check if there is any record from
status = true;
}
}
catch (Exception ex)
{
throw ex;
}
return status;
}
Note the above given approach to create the SQL connection and execute code should never be in the presentation layer project they should always be in a separate library and should always be referenced in the presentation layer project( hence separation of concern ) this can also be applied for the business logic code.
As you can see in the above code I have a Stored Procedure named as readDataFromXML
with 1 paramtere named as @data
with data-type as xml
.
And now to access the xml
in the SQL
side here is how the procedure will look like:
CREATE PROCEDURE readdatafromxml @data XML
AS
BEGIN
SELECT r.rc.value('(RecipeContributor/text())[1]', 'varchar(100)') AS
contributor,
r.rc.value('(RecipeDifficulty/text())[1]', 'varchar(100)') AS
difficulty,
r.rc.value('(RecipeRating/text())[1]', 'varchar(100)') AS
rating,
r.rc.value('(RecipeInstructions/text())[1]', 'varchar(100)') AS
instructions,
r.rc.value('(RecipeName/text())[1]', 'varchar(100)') AS
NAME,
r.rc.value('(RecipePrepTime/text())[1]', 'varchar(100)') AS
preptime,
r.rc.value('(RecipeCookTime/text())[1]', 'varchar(100)') AS
cooktime,
r.rc.value('(ImageURL/text())[1]', 'varchar(100)') AS
imgurl,
r.rc.value('(RecipeProtein/text())[1]', 'varchar(100)') AS
protien,
r.rc.value('(RecipeFats/text())[1]', 'varchar(100)') AS
fats,
r.rc.value('(RecipeCarbs/text())[1]', 'varchar(100)') AS
carbs
,
r.rc.value('(RecipeFiber/text())[1]', 'varchar(100)')
AS fiber,
r.rc.value('(RecipeDescription/text())[1]', 'varchar(100)') AS
reciepdescription
INTO #tmprecipe
FROM @data.nodes('/Rootobject/recipe') AS r(rc)
SELECT r.ri.value('(IngredientListQuanity/text())[1]', 'varchar(100)') AS
quantity,
r.ri.value('(MeasurementSizeName/text())[1]', 'varchar(100)') AS
sizename,
r.ri.value('(IngredientName/text())[1]', 'varchar(100)') AS
ingredientname
INTO #tmprecipeingrident
FROM @data.nodes('/Rootobject/recipeIngredients') AS r(ri)
--i am using a simple select just to check if there is some data in the temporary table you can change the code to match your need.
SELECT *
FROM #tmprecipe;
SELECT *
FROM #tmprecipeingrident;
--clearing the memory by dropping the temporary tables
DROP TABLE #tmprecipeingrident;
DROP TABLE #tmprecipe;
END
In the above stored procedure
I am passing the data from the xml
to a temporary table and accessing it from the xml
as:
FROM @data.nodes('/Rootobject/recipe') AS r(rc)
And here is the code to convert the given object into an XML
string:
public static string serializeListtoXML<T>(T obj)
{
System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(obj.GetType());
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
serializer.Serialize(ms, obj);
ms.Position = 0;
xmlDoc.Load(ms);
return xmlDoc.InnerXml;
}
}
In the above code I am creating an XMLDocument
passing the serialized object into that xml
document and then returning back the InnerXML
of that document.
Method 2 passing data as JSON ( use only in SQL-Server 2016 and above )
In order to pass the JSON
to SQL-Server
we have to make few changes to the above code as:
Remove the xml
attributes from the Rootobject
class and then change the code in the method postRootObject(Rootobject roots)
to serialize data into a JSON
string as:
[WebMethod]
public static string postRootObject(Rootobject roots)
{
try
{
var objectJsonString = JsonConvert.SerializeObject(roots);
//pass to stored procedure as
var statusSP = sendJsonToSqlServer("readDataFromJSON", objectJsonString);
return "yaay it works";
}
catch (Exception ex)
{
throw ex;
}
}
In the method sendJsonToSqlServer
keep everything as same as it was in sendXMLToSqlServer
because the data that we are sending will be a string
object which was same in case of XML
.Now the only changes you need to make in the stored procedure which will be as( work with JSON in SQL Server 2016 ):
create procedure readDataFromJSON @data nvarchar(4000)
as
begin
select
contributor,difficulty,rating,instructions,[name],preptime,cooktime,imgurl,protien,fats,carbs,fiber,reciepdescription
into #tmprecipe
from OPENJSON(@data,'$.Rootobject.recipe')
WITH (
RecipeContributor varchar(100) '$.RecipeContributor' as contributor,
RecipeDifficulty varchar(100) '$.RecipeDifficulty' as difficulty,
RecipeRating varchar(100) '$.RecipeRating' as rating,
RecipeInstructions varchar(100) '$.RecipeInstructions' as instructions,
RecipeName varchar(100) '$.RecipeName' as name,
RecipePrepTime varchar(100) '$.RecipePrepTime' as preptime,
RecipeCookTime varchar(100) '$.RecipeCookTime' as cooktime,
ImageURL varchar(100) '$.ImageURL' as imgurl,
RecipeProtein varchar(100) '$.RecipeProtein' as protien,
RecipeFats varchar(100) '$.RecipeFats' as fats,
RecipeCarbs varchar(100) '$.RecipeCarbs' as carbs,
RecipeFiber varchar(100) '$.RecipeFiber' as fibre,
RecipeDescription varchar(100) '$.RecipeDescription' as reciepdescription,
);
select
quantity,sizename,ingredientname
into #tmprecipeingrident
from OPENJSON(@data,'$.Rootobject.recipeIngredients')
WITH (
IngredientListQuanity varchar(100) '$.IngredientListQuanity' as quantity,
MeasurementSizeName varchar(100) '$.MeasurementSizeName' as sizename,
IngredientName varchar(100) '$.IngredientName' as ingredientname
);
select * from #tmprecipe;
select * from #tmprecipeingrident;
drop table #tmprecipeingrident;
drop table #tmprecipe;
end
Note: the above code is not tested as I do not have SQL-Server 2016. But as per the documentation provided by Microsoft it should work with some tweaks( if this does not work ).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.