简体   繁体   中英

Looking to send JSON object to Sql Server from aspx form

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM