Skip to content

Commit 88337a6

Browse files
committed
First commit
1 parent 91086b4 commit 88337a6

File tree

4 files changed

+371
-1
lines changed

4 files changed

+371
-1
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
The MIT License (MIT)
22

3-
Copyright (c) 2014 Matt
3+
Copyright (c) 2014 Matt Holt
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy of
66
this software and associated documentation files (the "Software"), to deal in

index.html

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>JSON-to-Go -- Convert JSON to Go type definitions</title>
5+
<script src="json-to-go.js"></script>
6+
<style>
7+
#output {
8+
white-space: pre;
9+
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
10+
font-size: 14px;
11+
line-height: 1.5em;
12+
}
13+
</style>
14+
</head>
15+
<body>
16+
<h1>JSON-to-Go</h1>
17+
<p>
18+
Translates JSON into a rough Go type definition.
19+
<br>
20+
Useful if you prefer to avoid <code>interface{}</code> and type assertions or writing the definitions yourself. (<a href="http://blog.golang.org/json-and-go" target="_blank">Read about JSON and Go.</a>)
21+
<br>
22+
The translator has to make some assumptions, so check the results (and use <code>go fmt</code>).
23+
</p>
24+
<input type="text" id="typename" placeholder="Name of type" size="40" value="ValidAddress">
25+
<br>
26+
<textarea rows="20" cols="75" id="input" placeholder="Paste valid JSON here">[
27+
{
28+
"input_index": 0,
29+
"candidate_index": 0,
30+
"addressee": "Apple Inc",
31+
"delivery_line_1": "1 Infinite Loop",
32+
"delivery_line_2": "PO Box 42",
33+
"last_line": "Cupertino CA 95014-2083",
34+
"delivery_point_barcode": "950142083017",
35+
"components": {
36+
"primary_number": "1",
37+
"street_name": "Infinite",
38+
"street_suffix": "Loop",
39+
"city_name": "Cupertino",
40+
"state_abbreviation": "CA",
41+
"zipcode": "95014",
42+
"plus4_code": "2083",
43+
"delivery_point": "01",
44+
"delivery_point_check_digit": "7"
45+
},
46+
"metadata": {
47+
"record_type": "S",
48+
"county_fips": "06085",
49+
"county_name": "Santa Clara",
50+
"carrier_route": "C067",
51+
"congressional_district": "15",
52+
"rdi": "Commercial",
53+
"latitude": 37.33118,
54+
"longitude": -122.03062,
55+
"precision": "Zip9"
56+
},
57+
"analysis": {
58+
"dpv_match_code": "Y",
59+
"dpv_footnotes": "AABB",
60+
"dpv_cmra": "N",
61+
"dpv_vacant": "N",
62+
"active": "Y"
63+
}
64+
},
65+
{
66+
"input_index": 0,
67+
"candidate_index": 0,
68+
"addressee": "Apple Inc",
69+
"delivery_line_1": "1 Infinite Loop",
70+
"delivery_line_2": "PO Box 42",
71+
"last_line": "Cupertino CA 95014-2083",
72+
"delivery_point_barcode": "950142083017",
73+
"components": {
74+
"primary_number": "1",
75+
"street_name": "Infinite",
76+
"street_suffix": "Loop",
77+
"city_name": "Cupertino",
78+
"state_abbreviation": "CA",
79+
"zipcode": "95014",
80+
"plus4_code": "2083",
81+
"delivery_point": "01",
82+
"delivery_point_check_digit": "7"
83+
},
84+
"metadata": {
85+
"record_type": "S",
86+
"county_fips": "06085",
87+
"county_name": "Santa Clara",
88+
"carrier_route": "C067",
89+
"congressional_district": "15",
90+
"rdi": "Commercial",
91+
"latitude": 37.33118,
92+
"longitude": -122.03062,
93+
"precision": "Zip9"
94+
},
95+
"analysis": {
96+
"dpv_match_code": "Y",
97+
"dpv_footnotes": "AABB",
98+
"dpv_cmra": "N",
99+
"dpv_vacant": "N",
100+
"active": "Y"
101+
}
102+
}
103+
]</textarea>
104+
<br>
105+
<i>(To demonstrate, the sample JSON is from <a href="http://smartystreets.com/products/liveaddress-api" target="_blank">LiveAddress API</a> by SmartyStreets.)</i>
106+
<br><br>
107+
108+
<button id="convert" onclick="doConvert()">Convert</button>
109+
110+
<br><br>
111+
112+
<div id="output"></div>
113+
114+
<br><br>
115+
<hr>
116+
<a href="https://github.com/mholt/json-to-go">JSON-to-Go on GitHub</a> by Matthew Holt
117+
118+
<script>
119+
var nameElem = document.getElementById('typename');
120+
var inputElem = document.getElementById('input');
121+
var outputElem = document.getElementById('output');
122+
function doConvert()
123+
{
124+
var result = jsonToGo(inputElem.value, nameElem.value);
125+
outputElem.textContent = result.error ? "ERROR: " + result.error : result.go;
126+
}
127+
</script>
128+
</body>
129+
</html>

json-to-go.js

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
/*
2+
JSON-to-Go
3+
by Matt Holt
4+
5+
https://github.com/mholt/json-to-go
6+
7+
A simple utility to translate JSON into a Go type definition.
8+
*/
9+
10+
function jsonToGo(json, typename)
11+
{
12+
var data;
13+
var scope;
14+
var go = "";
15+
tabs = 0;
16+
17+
try
18+
{
19+
data = JSON.parse(json);
20+
scope = data;
21+
}
22+
catch (e)
23+
{
24+
return {
25+
go: "",
26+
error: e.message
27+
};
28+
}
29+
30+
typename = format(typename);
31+
32+
if (!typename)
33+
typename = "GIVE_ME_A_NAME";
34+
35+
append("type "+typename+" ");
36+
37+
parseScope(scope);
38+
39+
return { go: go };
40+
41+
42+
43+
function parseScope(scope)
44+
{
45+
if (typeof scope === "object")
46+
{
47+
if (Array.isArray(scope))
48+
{
49+
var sliceType;
50+
for (var i = 0; i < scope.length; i++)
51+
{
52+
var thisType = goType(scope[i]);
53+
if (!sliceType)
54+
sliceType = thisType;
55+
else if (sliceType != thisType)
56+
{
57+
sliceType = mostSpecificPossibleGoType(thisType, sliceType);
58+
if (sliceType == "interface{}")
59+
break;
60+
}
61+
}
62+
append("[]");
63+
if (sliceType == "struct")
64+
parseScope(scope[0]);
65+
else
66+
append(sliceType);
67+
}
68+
else
69+
{
70+
append("struct {\n");
71+
++tabs;
72+
var keys = Object.keys(scope);
73+
for (var i in keys)
74+
{
75+
var keyname = keys[i];
76+
indent(tabs);
77+
append(format(keyname)+" ");
78+
parseScope(scope[keyname]);
79+
append(' `json:"'+keyname+'"`\n');
80+
}
81+
indent(--tabs);
82+
append("}");
83+
}
84+
}
85+
else
86+
append(goType(scope));
87+
}
88+
89+
function indent(tabs)
90+
{
91+
for (var i = 0; i < tabs; i++)
92+
go += '\t';
93+
}
94+
95+
function append(str)
96+
{
97+
go += str;
98+
}
99+
100+
function format(str)
101+
{
102+
return toProperCase(str.replace(/\s/g, "_")).replace(/_/g, "");
103+
}
104+
105+
function goType(val)
106+
{
107+
switch (typeof val)
108+
{
109+
case "string":
110+
return "string";
111+
case "number":
112+
if (val % 1 === 0)
113+
{
114+
if (val > -2147483648 && val < 2147483647)
115+
return "int"
116+
else
117+
return "int64"
118+
}
119+
else
120+
return "float32"
121+
case "object":
122+
return "struct";
123+
case "array":
124+
return "slice";
125+
default:
126+
return "interface{}";
127+
}
128+
}
129+
130+
function mostSpecificPossibleGoType(typ1, typ2)
131+
{
132+
if (typ1.substr(0, 5) == "float"
133+
&& typ2.substr(0, 3) == "int")
134+
return typ1;
135+
else if (typ1.substr(0, 3) == "int"
136+
&& typ2.substr(0, 5) == "float")
137+
return typ1;
138+
else
139+
{
140+
return "interface{}";
141+
}
142+
}
143+
144+
// Thanks to http://stackoverflow.com/a/5574446/1048862
145+
function toProperCase(str)
146+
{
147+
return str.replace(/[^_]*/gi, function(txt)
148+
{
149+
return txt.charAt(0).toUpperCase()
150+
+ txt.substr(1).toLowerCase();
151+
});
152+
}
153+
}

sample.json

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
[
2+
{
3+
"input_index": 0,
4+
"candidate_index": 0,
5+
"delivery_line_1": "1 N Rosedale St",
6+
"last_line": "Baltimore MD 21229-3737",
7+
"delivery_point_barcode": "212293737013",
8+
"components": {
9+
"primary_number": "1",
10+
"street_predirection": "N",
11+
"street_name": "Rosedale",
12+
"street_suffix": "St",
13+
"city_name": "Baltimore",
14+
"state_abbreviation": "MD",
15+
"zipcode": "21229",
16+
"plus4_code": "3737",
17+
"delivery_point": "01",
18+
"delivery_point_check_digit": "3"
19+
},
20+
"metadata": {
21+
"record_type": "S",
22+
"zip_type": "Standard",
23+
"county_fips": "24510",
24+
"county_name": "Baltimore City",
25+
"carrier_route": "C047",
26+
"congressional_district": "07",
27+
"rdi": "Residential",
28+
"elot_sequence": "0059",
29+
"elot_sort": "A",
30+
"latitude": 39.28602,
31+
"longitude": -76.6689,
32+
"precision": "Zip9",
33+
"time_zone": "Eastern",
34+
"utc_offset": -5,
35+
"dst": true
36+
},
37+
"analysis": {
38+
"dpv_match_code": "Y",
39+
"dpv_footnotes": "AABB",
40+
"dpv_cmra": "N",
41+
"dpv_vacant": "N",
42+
"active": "Y"
43+
}
44+
},
45+
{
46+
"input_index": 0,
47+
"candidate_index": 1,
48+
"delivery_line_1": "1 S Rosedale St",
49+
"last_line": "Baltimore MD 21229-3739",
50+
"delivery_point_barcode": "212293739011",
51+
"components": {
52+
"primary_number": "1",
53+
"street_predirection": "S",
54+
"street_name": "Rosedale",
55+
"street_suffix": "St",
56+
"city_name": "Baltimore",
57+
"state_abbreviation": "MD",
58+
"zipcode": "21229",
59+
"plus4_code": "3739",
60+
"delivery_point": "01",
61+
"delivery_point_check_digit": "1"
62+
},
63+
"metadata": {
64+
"record_type": "S",
65+
"zip_type": "Standard",
66+
"county_fips": "24510",
67+
"county_name": "Baltimore City",
68+
"carrier_route": "C047",
69+
"congressional_district": "07",
70+
"rdi": "Residential",
71+
"elot_sequence": "0064",
72+
"elot_sort": "A",
73+
"latitude": 39.2858,
74+
"longitude": -76.66889,
75+
"precision": "Zip9",
76+
"time_zone": "Eastern",
77+
"utc_offset": -5,
78+
"dst": true
79+
},
80+
"analysis": {
81+
"dpv_match_code": "Y",
82+
"dpv_footnotes": "AABB",
83+
"dpv_cmra": "N",
84+
"dpv_vacant": "N",
85+
"active": "Y"
86+
}
87+
}
88+
]

0 commit comments

Comments
 (0)