jQuery, Convert HTML Table to JSON - Depricated, See V2!
The problem:
In situations where you are allowing the user to edit html tables client side by adding or removing rows or editing existing rows, you find that you need a way to propagate these changes back to the business layer and downstream to the database as the case may be. At first thought, you might consider that upon post, you could just access your changes server side. This is not usually possible. Or you might not want to post, what will you do? You could write script to ramble your way through the table and glean the data and try to chunk it into some sort of format that you can send to the server, but why do that when you can use JSON and the tabletojson jQuery plugin?
The idea:
What I usually do in a case like this is convert the grid to JSON in the format that JSON.NETunderstands so that I can serialize this directly to a business object to be used by .NET. It’s a sweet and light way to do this in my opinion and it’s fairly easy to implement. It works for any grid control that renders as a table using at least THEAD and TBODY tags. The steps are as follows after you have configured the plug-in:
1) User edits the table.
2) On save you can attach a client side event to the ASP.NET save button onClientClick and you can also attach whatever server side event you want (OnClick). As it turns out, the client side function will be ran first and if it returns false, the server side event will not be ran. Nice fact to know when doing client side validation, or the like. I use this to bundle my table in a JSON string and write it to a hidden field for the purpose of posting to the server. NOTE: I just so happen to be using this hidden field for posting, but you could easily pass the JSON via a jQuery AJAX call to a web service method or web method. The would be a good reason to use the built in callback function to submit the data upon JSON conversion completion.
3) On the server side, snag the JSON string from the hidden field and run the JSON.NET serialize function. This will populate your business object and you’re good to go.
But wait, there’s more:
So, suppose, the business object has fieldA, fieldB and fieldC and I actually display Name, Date, and Load as field names to the user. Well, without configuring the plug-in, it will just use the fields names from THEAD (fieldA, fieldB and fieldC), but if you need something different, I offer a configuration property called headers. If you pass this your custom headers comma delimited, it will use these instead!
OK, so…that’s fairly deluxe I think, but now, what if you wanted to tuck extra data that you aren’t showing to the user somewhere in the grid and have this be picked up by the plug-in as well? Well, I offer another configuration property called attribHeaders. This one is a tad different, but essentially, if you pass in JSON style name-value pairs so that the plug-in knows what attribute(s) to look for and what you want the custom field name to be, it will include these as fields in the JSON object.
OK, so now what?
So, now you may wonder how you get the JSON string from this plug-in…I offer three fun filled solutions. The first is just by assigning the return to a variable. This is fine if you don’t mind breaking the jQuery chain. The second is, if you pass in an object like say ‘#hiddenfieldX’ to the returnElement configuration property, it will just dump directly there. If you need more than that, you can pass a callback function to the configuration property called complete. This allows for an asynchronous callback, passing the JSON string to your function for you to do with as you please as soon as it’s both good and ready.
The Code:
01.
(
function
($) {
02.
$.fn.tabletojson =
function
(options) {
03.
var
defaults = {
04.
headers:
null
,
05.
attribHeaders:
null
,
06.
returnElement:
null
,
07.
complete:
null
08.
};
09.
10.
var
options = $.extend(defaults, options);
11.
var
selector =
this
;
12.
var
jsonRowItem =
""
;
13.
var
jsonItem =
new
Array();
14.
var
jsonRow =
new
Array();
15.
var
heads = [];
16.
var
rowCounter = 1;
17.
var
comma =
","
;
18.
var
json =
""
;
19.
20.
if
(options.headers !=
null
) {
21.
options.headers = options.headers.split(
' '
).join(
''
);
22.
heads = options.headers.split(
","
);
23.
}
24.
25.
var
rows = $(
":not(tfoot) > tr"
,
this
).length;
26.
$(
":not(tfoot) > tr"
,
this
).each(
function
(i, tr) {
27.
jsonRowItem =
""
;
28.
29.
if
(
this
.parentNode.tagName ==
"TFOOT"
) {
30.
return
;
31.
}
32.
if
(
this
.parentNode.tagName ==
"THEAD"
) {
33.
if
(options.headers ==
null
) {
34.
$(
'th'
, tr).each(
function
(i, th) {
35.
heads[heads.length] = $(th).html();
36.
});
37.
}
38.
}
39.
else
{
40.
41.
if
(options.attribHeaders !=
null
) {
42.
var
h = eval(
"("
+ options.attribHeaders +
")"
);
43.
44.
for
(z
in
h) {
45.
heads[heads.length] = h[z];
46.
}
47.
}
48.
49.
rowCounter++
50.
var
headCounter = 0;
51.
jsonRowItem =
"{"
;
52.
jsonItem.length = 0;
53.
$(
'td'
, tr).each(
function
(i, td) {
54.
var
re = / /gi;
55.
var
v = $(td).html().replace(re,
''
)
56.
jsonItem[jsonItem.length] =
"\""
+ heads[headCounter] +
"\":\""
+ v +
"\""
;
57.
headCounter++;
58.
});
59.
60.
if
(options.attribHeaders !=
null
) {
61.
for
(z
in
h) {
62.
jsonItem[jsonItem.length] =
"\""
+ heads[headCounter] +
"\":\""
+ tr[z] +
"\""
;
63.
headCounter++;
64.
}
65.
}
66.
67.
jsonRowItem += jsonItem.join(
","
);
68.
jsonRowItem +=
"}"
;
69.
jsonRow[jsonRow.length] = jsonRowItem;
70.
}
71.
});
72.
json +=
"["
+ jsonRow.join(
","
) +
"]"
;
73.
74.
if
(options.complete !=
null
) {
75.
options.complete(json);
76.
}
77.
78.
if
(options.returnElement ==
null
)
79.
return
json;
80.
else
{
81.
$(options.returnElement).val(json);
82.
return
this
;
83.
}
84.
85.
}
86.
})(jQuery)
The HTML implementation:
Points of interest here are that I have added some attributes to the rows, namely customerIDand orderID. These aren’t real HTML attributes, but that doesn’t matter, they can still be used.
01.
<
table
id
=
"superTable"
summary
=
"Employee Pay Sheet"
>
02.
<
thead
>
03.
<
tr
>
04.
<
th
>Employee</
th
>
05.
<
th
>Salary</
th
>
06.
<
th
>Bonus</
th
>
07.
<
th
>Supervisor</
th
>
08.
</
tr
>
09.
</
thead
>
10.
<
tbody
>
11.
<
tr
customerID
=
"222"
orderID
=
"1222"
>
12.
<
td
>Stephen C. Cox</
td
>
13.
<
td
>$300</
td
>
14.
<
td
>$50</
td
>
15.
<
td
>Bob</
td
>
16.
</
tr
>
17.
<
tr
customerID
=
"223"
orderID
=
"1223"
>
18.
<
td
>Josephin Tan</
td
>
19.
<
td
>$150</
td
>
20.
<
td
>-</
td
>
21.
<
td
>Annie</
td
>
22.
</
tr
>
23.
<
tr
customerID
=
"224"
orderID
=
"1224"
>
24.
<
td
>Joyce Ming</
td
>
25.
<
td
>$200</
td
>
26.
<
td
>$35</
td
>
27.
<
td
>Andy</
td
>
28.
</
tr
>
29.
<
tr
customerID
=
"225"
orderID
=
"1225"
>
30.
<
td
>James A. Pentel</
td
>
31.
<
td
>$175</
td
>
32.
<
td
>$25</
td
>
33.
<
td
>Annie</
td
>
34.
</
tr
>
35.
</
tbody
>
36.
</
table
>
37.
<
input
type
=
"hidden"
id
=
"hf"
/>
Script Implementation:
I suggest that you put script that runs on load after the markup just to make sure that your markup has been rendered before the script tries to work with it. This is an example of both dumping the data to the hidden field ‘hf’ as well as passing the json string via x to the anonymous function. I then alert the value. I could also have just said var json = $(“#superTable).tabletojson(… and that would have return it to json, but…the jQuery chain would be broken. :(
01.
<script type=
"text/javascript"
>
02.
$(
"#superTable"
).tabletojson({
03.
headers:
"Employee1,Salary1,Bonus1,Supervisor1"
,
04.
attribHeaders:
"{'customerID':'CustomerID','orderID':'OrderID'}"
,
05.
returnElement:
"#hf"
,
06.
complete:
function
(x){
07.
alert(x);
08.
}
09.
})
10.
</script>
JSON Output:
Notice here that the attributes have been added using the custom field names as well as the custom field names as indicated for the table headers.
01.
[{"Employee1":"Stephen C. Cox",
02.
"Salary1":"$300",
03.
"Bonus1":"$50",
04.
"Supervisor1":"Bob",
05.
"CustomerID":"222",
06.
"OrderID":"1222"},
07.
{"Employee1":"Josephin Tan",
08.
"Salary1":"$150",
09.
"Bonus1":"-",
10.
"Supervisor1":"Annie",
11.
"CustomerID":"223",
12.
"OrderID":"1223"},
13.
{"Employee1":"Joyce Ming",
14.
"Salary1":"$200",
15.
"Bonus1":"$35",
16.
"Supervisor1":"Andy",
17.
"CustomerID":"224",
18.
"OrderID":"1224"},
19.
{"Employee1":"James A. Pentel",
20.
"Salary1":"$175",
21.
"Bonus1":"$25",
22.
"Supervisor1":"Annie",
23.
"CustomerID":"225",
24.
"OrderID":"1225"}]
Additionally:
Javascript usage:
I had mentioned earlier that you can use this with JSON.NET, but we are in the wide world of Javascript so we can eval the JSON and use it as an object…check this out:
This is showing the results from eval(x)…x being my json string.
JSON.NET usage:
I need to create or have had created an object like the one below to map up with the JSON object. I say this because you can either make the .NET object match the JSON object or visa versa.
01.
public
struct
NuggetData
02.
{
03.
public
string
Bonus1 {
get
;
set
; }
04.
public
string
CustomerID {
get
;
set
; }
05.
public
string
Employee1 {
get
;
set
; }
06.
public
string
OrderID {
get
;
set
; }
07.
public
string
Salary1 {
get
;
set
; }
08.
public
string
Supervisor1 {
get
;
set
; }
09.
}
After you do this you can run the following code:
1.
private
List<NuggetData> _NuggetList;
2.
3.
private
void
Save()
4.
{
5.
string
dataVals = Request.Form[hf.UniqueID];
6.
_NuggetList;= JsonConvert.DeserializeObject<List<NuggetData>>(dataVals);
7.
8.
}
Notice here that I snag the JSON string from the hidden field “hf” and then I serialize to the list of Nuggets.
Ding! Fries are done! Download and try it out!
jQuery, JSONHTML Table to JSON
관계된 작성글
전체 코멘트
Made a slight modification, since I have actual html elements in the table cells that can be selected. I am no JS guru, so there is MOST likely a better way for doing this:
var v;
if (this.children.length > 0) {
v = this.firstElementChild.value;
}
else
v = $(td).html().replace(re, '');
Anyways, thanks a lot, great help!
one small issue though.the way you are reading attributes when attribHeaders is specified,doesn't work in firefox.
so i had to change the line
jsonItem[jsonItem.length] = "\"" + heads[headCounter] + "\":\"" +tr.attributes[z].value + "\""
and i had a another small requirement of ignoring few columns from the table like buttons/images column.so i have added another option named "ignoreColumns" which takes comma seperated column indexes.
If anyone interested,let me know.i can share the code.
Best,
Chris