├── readme.png ├── favicon.ico ├── LICENSE ├── README.md ├── styles └── default.css ├── styles.css ├── index.html ├── highlight.pack.js ├── code.js └── jsoneditor.min.css /readme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashamp/jsonToDartModel/HEAD/readme.png -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashamp/jsonToDartModel/HEAD/favicon.ico -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 ashamp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsonToDartModel 2 | 3 | online tool for convert json to dart code 4 | 5 | click [https://ashamp.github.io/jsonToDartModel/](https://ashamp.github.io/jsonToDartModel/) 6 | 7 | ## Feature 8 | - online use, without plugin 9 | - support null safe 10 | - support multidimensional list 11 | - support complex json 12 | - support convert all props to String type 13 | - empty props warning 14 | - single file 15 | - dart keyword protected 16 | - instant convert 17 | 18 | ## FYI 19 | - object should have at least one property 20 | - only first object in array will be parsed, empty array will cause error 21 | - when select `Force String Type` , the `bool` type will not convert 22 | 23 | ## Usage 24 | 1. input json string in left textinput 25 | 2. input root class name in left bottom textinput 26 | 3. copy code by button or mouse 27 | 28 | ## Example 29 | json string may looks like 30 | ``` json 31 | { 32 | "anInt": 1, 33 | "aDouble": 2.3, 34 | "aString": "hello", 35 | "aBool": false, 36 | "anObj": { 37 | "name": "x", 38 | "age": 18.0 39 | } 40 | } 41 | ``` 42 | named it `SomeRootEntity` and convert to dart 43 | ``` dart 44 | var obj = SomeRootEntity.fromJson(jsonDecode(json)); 45 | String encodedJson = jsonEncode(obj.toJson()); 46 | print(encodedJson);//{"anInt":1,"aDouble":2.3,"aString":"hello","aBool":false,"anObj":{"name":"x","age":18.0}} 47 | ``` 48 | 49 | ![reademe](readme.png) 50 | -------------------------------------------------------------------------------- /styles/default.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Original highlight.js style (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | background: #F0F0F0; 12 | } 13 | 14 | 15 | /* Base color: saturation 0; */ 16 | 17 | .hljs, 18 | .hljs-subst { 19 | color: #444; 20 | } 21 | 22 | .hljs-comment { 23 | color: #888888; 24 | } 25 | 26 | .hljs-keyword, 27 | .hljs-attribute, 28 | .hljs-selector-tag, 29 | .hljs-meta-keyword, 30 | .hljs-doctag, 31 | .hljs-name { 32 | font-weight: bold; 33 | } 34 | 35 | 36 | /* User color: hue: 0 */ 37 | 38 | .hljs-type, 39 | .hljs-string, 40 | .hljs-number, 41 | .hljs-selector-id, 42 | .hljs-selector-class, 43 | .hljs-quote, 44 | .hljs-template-tag, 45 | .hljs-deletion { 46 | color: #880000; 47 | } 48 | 49 | .hljs-title, 50 | .hljs-section { 51 | color: #880000; 52 | font-weight: bold; 53 | } 54 | 55 | .hljs-regexp, 56 | .hljs-symbol, 57 | .hljs-variable, 58 | .hljs-template-variable, 59 | .hljs-link, 60 | .hljs-selector-attr, 61 | .hljs-selector-pseudo { 62 | color: #BC6060; 63 | } 64 | 65 | 66 | /* Language color: hue: 90; */ 67 | 68 | .hljs-literal { 69 | color: #78A960; 70 | } 71 | 72 | .hljs-built_in, 73 | .hljs-bullet, 74 | .hljs-code, 75 | .hljs-addition { 76 | color: #397300; 77 | } 78 | 79 | 80 | /* Meta color: hue: 200 */ 81 | 82 | .hljs-meta { 83 | color: #1f7199; 84 | } 85 | 86 | .hljs-meta-string { 87 | color: #4d99bf; 88 | } 89 | 90 | 91 | /* Misc effects */ 92 | 93 | .hljs-emphasis { 94 | font-style: italic; 95 | } 96 | 97 | .hljs-strong { 98 | font-weight: bold; 99 | } 100 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0px; 3 | padding: 0px; 4 | list-style: none; 5 | /* border: 1px solid blue; */ 6 | } 7 | 8 | body, 9 | .mainContent { 10 | position: absolute; 11 | left: 2px; 12 | right: 2px; 13 | top: 2px; 14 | bottom: 2px; 15 | } 16 | 17 | .mainContent { 18 | padding: 2px; 19 | flex-direction: column; 20 | flex: 1; 21 | } 22 | 23 | .divContainer { 24 | display: flex; 25 | overflow: auto; 26 | padding: 2px; 27 | border: 1px solid #999999; 28 | } 29 | 30 | .mainContainer{ 31 | flex: 1; 32 | margin-top: 2px; 33 | } 34 | 35 | .info{ 36 | background: #FF8040; 37 | color: #ffffff; 38 | } 39 | 40 | .leftContainer{ 41 | flex: 1; 42 | flex-direction: column; 43 | } 44 | 45 | .rightContainer{ 46 | flex: 1; 47 | margin-left: 2px; 48 | flex-direction: column 49 | } 50 | 51 | .jsonContainer{ 52 | flex: 1 53 | } 54 | 55 | .origJsonContainer{ 56 | flex: 1 57 | } 58 | 59 | .inputsContainer{ 60 | margin-top: 2px; 61 | flex-direction: column; 62 | } 63 | 64 | .inputTitle{ 65 | width: 150px; 66 | } 67 | 68 | .textInputContainer{ 69 | height: 40px; 70 | } 71 | 72 | .inputContainer{ 73 | flex: 1; 74 | flex-direction: row; 75 | } 76 | 77 | input[type='text']{ 78 | flex: 1; 79 | } 80 | .objcHeader{ 81 | flex: 2; 82 | } 83 | .objcImpl{ 84 | flex: 3; 85 | margin-top: 2px; 86 | } 87 | 88 | textarea{ 89 | resize: none; 90 | flex: 1; 91 | } 92 | p{ 93 | align-self: center 94 | } 95 | 96 | #genBtn{ 97 | width: 60px; 98 | } 99 | 100 | .copyBtnContaner{ 101 | justify-content: center; 102 | align-items: center; 103 | } 104 | 105 | .copyBtn{ 106 | width: 100%; 107 | height: 40px; 108 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | Json To Dart Model 14 | 15 | 16 | 17 |
18 |
19 | 20 |

Json To Dart Model Code Generator

21 | view source 22 |
23 |
24 | 25 | 26 |
27 |
28 | 29 |
30 | 31 |
32 | 33 |
34 | 35 | 36 |
37 |
38 |
39 |
40 | 41 |
42 |
43 |

JsonKey:

44 |
45 |
46 |

Json key string

47 |

  |  

48 |

Private

49 |
50 |
51 |
52 |
53 |

Camel Case:

54 |
55 |
56 |

Convert snake case to camel case

57 |
58 |
59 |
60 |
61 |

Null safe:

62 |
63 |
64 |

Enable null safe

65 |
66 |
67 |
68 |
69 |

Fault tolerance:

70 |
71 |
72 |

Fault tolerance for JSON with mismatched data types

73 |
74 |
75 |
76 |
77 |

Force String Type:

78 |
79 |
80 |

Convert all props to String type (Except bool)

81 |
82 |
83 |
84 |
85 |

Store Original Json:

86 |
87 |
88 |

Enable store original Json

89 |
90 |
91 |
92 |
93 |

Root Class Name:

94 |
95 |
96 | 97 |
98 |
99 |
100 |
101 |

File Name:

102 |
103 |
104 | 105 |
106 |
107 |
108 |
109 |
110 | 111 |
112 | 113 |
114 |
115 |
116 | 117 |
118 |
119 |
120 |
121 | 122 | 123 | 124 | 125 | 126 |
127 | 128 |
129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /highlight.pack.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.15.8 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(a){var f=[],u=Object.keys,N={},c={},n=/^(no-?highlight|plain|text)$/i,s=/\blang(?:uage)?-([\w-]+)\b/i,t=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,r={case_insensitive:"cI",lexemes:"l",contains:"c",keywords:"k",subLanguage:"sL",className:"cN",begin:"b",beginKeywords:"bK",end:"e",endsWithParent:"eW",illegal:"i",excludeBegin:"eB",excludeEnd:"eE",returnBegin:"rB",returnEnd:"rE",relevance:"r",variants:"v",IDENT_RE:"IR",UNDERSCORE_IDENT_RE:"UIR",NUMBER_RE:"NR",C_NUMBER_RE:"CNR",BINARY_NUMBER_RE:"BNR",RE_STARTERS_RE:"RSR",BACKSLASH_ESCAPE:"BE",APOS_STRING_MODE:"ASM",QUOTE_STRING_MODE:"QSM",PHRASAL_WORDS_MODE:"PWM",C_LINE_COMMENT_MODE:"CLCM",C_BLOCK_COMMENT_MODE:"CBCM",HASH_COMMENT_MODE:"HCM",NUMBER_MODE:"NM",C_NUMBER_MODE:"CNM",BINARY_NUMBER_MODE:"BNM",CSS_NUMBER_MODE:"CSSNM",REGEXP_MODE:"RM",TITLE_MODE:"TM",UNDERSCORE_TITLE_MODE:"UTM",COMMENT:"C",beginRe:"bR",endRe:"eR",illegalRe:"iR",lexemesRe:"lR",terminators:"t",terminator_end:"tE"},b="",h={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};function _(e){return e.replace(/&/g,"&").replace(//g,">")}function E(e){return e.nodeName.toLowerCase()}function v(e,n){var t=e&&e.exec(n);return t&&0===t.index}function l(e){return n.test(e)}function g(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function R(e){var a=[];return function e(n,t){for(var r=n.firstChild;r;r=r.nextSibling)3===r.nodeType?t+=r.nodeValue.length:1===r.nodeType&&(a.push({event:"start",offset:t,node:r}),t=e(r,t),E(r).match(/br|hr|img|input/)||a.push({event:"stop",offset:t,node:r}));return t}(e,0),a}function i(e){if(r&&!e.langApiRestored){for(var n in e.langApiRestored=!0,r)e[n]&&(e[r[n]]=e[n]);(e.c||[]).concat(e.v||[]).forEach(i)}}function m(o){function s(e){return e&&e.source||e}function c(e,n){return new RegExp(s(e),"m"+(o.cI?"i":"")+(n?"g":""))}!function n(t,e){if(!t.compiled){if(t.compiled=!0,t.k=t.k||t.bK,t.k){function r(t,e){o.cI&&(e=e.toLowerCase()),e.split(" ").forEach(function(e){var n=e.split("|");a[n[0]]=[t,n[1]?Number(n[1]):1]})}var a={};"string"==typeof t.k?r("keyword",t.k):u(t.k).forEach(function(e){r(e,t.k[e])}),t.k=a}t.lR=c(t.l||/\w+/,!0),e&&(t.bK&&(t.b="\\b("+t.bK.split(" ").join("|")+")\\b"),t.b||(t.b=/\B|\b/),t.bR=c(t.b),t.endSameAsBegin&&(t.e=t.b),t.e||t.eW||(t.e=/\B|\b/),t.e&&(t.eR=c(t.e)),t.tE=s(t.e)||"",t.eW&&e.tE&&(t.tE+=(t.e?"|":"")+e.tE)),t.i&&(t.iR=c(t.i)),null==t.r&&(t.r=1),t.c||(t.c=[]),t.c=Array.prototype.concat.apply([],t.c.map(function(e){return function(n){return n.v&&!n.cached_variants&&(n.cached_variants=n.v.map(function(e){return g(n,{v:null},e)})),n.cached_variants||n.eW&&[g(n)]||[n]}("self"===e?t:e)})),t.c.forEach(function(e){n(e,t)}),t.starts&&n(t.starts,e);var i=t.c.map(function(e){return e.bK?"\\.?(?:"+e.b+")\\.?":e.b}).concat([t.tE,t.i]).map(s).filter(Boolean);t.t=i.length?c(function(e,n){for(var t=/\[(?:[^\\\]]|\\.)*\]|\(\??|\\([1-9][0-9]*)|\\./,r=0,a="",i=0;i')+n+(t?"":b):n}function o(){E+=null!=l.sL?function(){var e="string"==typeof l.sL;if(e&&!N[l.sL])return _(g);var n=e?C(l.sL,g,!0,f[l.sL]):O(g,l.sL.length?l.sL:void 0);return 0")+'"');return g+=n,n.length||1}var s=B(e);if(!s)throw new Error('Unknown language: "'+e+'"');m(s);var a,l=t||s,f={},E="";for(a=l;a!==s;a=a.parent)a.cN&&(E=c(a.cN,"",!0)+E);var g="",R=0;try{for(var d,p,M=0;l.t.lastIndex=M,d=l.t.exec(n);)p=r(n.substring(M,d.index),d[0]),M=d.index+p;for(r(n.substr(M)),a=l;a.parent;a=a.parent)a.cN&&(E+=b);return{r:R,value:E,language:e,top:l}}catch(e){if(e.message&&-1!==e.message.indexOf("Illegal"))return{r:0,value:_(n)};throw e}}function O(t,e){e=e||h.languages||u(N);var r={r:0,value:_(t)},a=r;return e.filter(B).filter(M).forEach(function(e){var n=C(e,t,!1);n.language=e,n.r>a.r&&(a=n),n.r>r.r&&(a=r,r=n)}),a.language&&(r.second_best=a),r}function d(e){return h.tabReplace||h.useBR?e.replace(t,function(e,n){return h.useBR&&"\n"===e?"
":h.tabReplace?n.replace(/\t/g,h.tabReplace):""}):e}function o(e){var n,t,r,a,i,o=function(e){var n,t,r,a,i=e.className+" ";if(i+=e.parentNode?e.parentNode.className:"",t=s.exec(i))return B(t[1])?t[1]:"no-highlight";for(n=0,r=(i=i.split(/\s+/)).length;n/g,"\n"):n=e,i=n.textContent,r=o?C(o,i,!0):O(i),(t=R(n)).length&&((a=document.createElementNS("http://www.w3.org/1999/xhtml","div")).innerHTML=r.value,r.value=function(e,n,t){var r=0,a="",i=[];function o(){return e.length&&n.length?e[0].offset!==n[0].offset?e[0].offset"}function u(e){a+=""}function s(e){("start"===e.event?c:u)(e.node)}for(;e.length||n.length;){var l=o();if(a+=_(t.substring(r,l[0].offset)),r=l[0].offset,l===e){for(i.reverse().forEach(u);s(l.splice(0,1)[0]),(l=o())===e&&l.length&&l[0].offset===r;);i.reverse().forEach(c)}else"start"===l[0].event?i.push(l[0].node):i.pop(),s(l.splice(0,1)[0])}return a+_(t.substr(r))}(t,R(a),i)),r.value=d(r.value),e.innerHTML=r.value,e.className=function(e,n,t){var r=n?c[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}(e.className,o,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function p(){if(!p.called){p.called=!0;var e=document.querySelectorAll("pre code");f.forEach.call(e,o)}}function B(e){return e=(e||"").toLowerCase(),N[e]||N[c[e]]}function M(e){var n=B(e);return n&&!n.disableAutodetect}return a.highlight=C,a.highlightAuto=O,a.fixMarkup=d,a.highlightBlock=o,a.configure=function(e){h=g(h,e)},a.initHighlighting=p,a.initHighlightingOnLoad=function(){addEventListener("DOMContentLoaded",p,!1),addEventListener("load",p,!1)},a.registerLanguage=function(n,e){var t=N[n]=e(a);i(t),t.aliases&&t.aliases.forEach(function(e){c[e]=n})},a.listLanguages=function(){return u(N)},a.getLanguage=B,a.autoDetection=M,a.inherit=g,a.IR=a.IDENT_RE="[a-zA-Z]\\w*",a.UIR=a.UNDERSCORE_IDENT_RE="[a-zA-Z_]\\w*",a.NR=a.NUMBER_RE="\\b\\d+(\\.\\d+)?",a.CNR=a.C_NUMBER_RE="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",a.BNR=a.BINARY_NUMBER_RE="\\b(0b[01]+)",a.RSR=a.RE_STARTERS_RE="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",a.BE=a.BACKSLASH_ESCAPE={b:"\\\\[\\s\\S]",r:0},a.ASM=a.APOS_STRING_MODE={cN:"string",b:"'",e:"'",i:"\\n",c:[a.BE]},a.QSM=a.QUOTE_STRING_MODE={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE]},a.PWM=a.PHRASAL_WORDS_MODE={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},a.C=a.COMMENT=function(e,n,t){var r=a.inherit({cN:"comment",b:e,e:n,c:[]},t||{});return r.c.push(a.PWM),r.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),r},a.CLCM=a.C_LINE_COMMENT_MODE=a.C("//","$"),a.CBCM=a.C_BLOCK_COMMENT_MODE=a.C("/\\*","\\*/"),a.HCM=a.HASH_COMMENT_MODE=a.C("#","$"),a.NM=a.NUMBER_MODE={cN:"number",b:a.NR,r:0},a.CNM=a.C_NUMBER_MODE={cN:"number",b:a.CNR,r:0},a.BNM=a.BINARY_NUMBER_MODE={cN:"number",b:a.BNR,r:0},a.CSSNM=a.CSS_NUMBER_MODE={cN:"number",b:a.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},a.RM=a.REGEXP_MODE={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[a.BE,{b:/\[/,e:/\]/,r:0,c:[a.BE]}]},a.TM=a.TITLE_MODE={cN:"title",b:a.IR,r:0},a.UTM=a.UNDERSCORE_TITLE_MODE={cN:"title",b:a.UIR,r:0},a.METHOD_GUARD={b:"\\.\\s*"+a.UIR,r:0},a});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("xml",function(s){var e={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("\x3c!--","--\x3e",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"meta",b:/<\?xml/,e:/\?>/,r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0},{b:'b"',e:'"',skip:!0},{b:"b'",e:"'",skip:!0},s.inherit(s.ASM,{i:null,cN:null,c:null,skip:!0}),s.inherit(s.QSM,{i:null,cN:null,c:null,skip:!0})]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[e],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[e],starts:{e:"<\/script>",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},e]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^\\s*([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}|\t)",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});hljs.registerLanguage("dart",function(e){var t={cN:"subst",v:[{b:"\\$[A-Za-z0-9_]+"}]},r={cN:"subst",v:[{b:"\\${",e:"}"}],k:"true false null this is new super"},n={cN:"string",v:[{b:"r'''",e:"'''"},{b:'r"""',e:'"""'},{b:"r'",e:"'",i:"\\n"},{b:'r"',e:'"',i:"\\n"},{b:"'''",e:"'''",c:[e.BE,t,r]},{b:'"""',e:'"""',c:[e.BE,t,r]},{b:"'",e:"'",i:"\\n",c:[e.BE,t,r]},{b:'"',e:'"',i:"\\n",c:[e.BE,t,r]}]};r.c=[e.CNM,n];return{k:{keyword:"assert async await break case catch class const continue default do else enum extends false final finally for if in is new null rethrow return super switch sync this throw true try var void while with yield abstract as dynamic export external factory get implements import library operator part set static typedef",built_in:"print Comparable DateTime Duration Function Iterable Iterator List Map Match Null Object Pattern RegExp Set Stopwatch String StringBuffer StringSink Symbol Type Uri bool double int num document window querySelector querySelectorAll Element ElementList"},c:[n,e.C("/\\*\\*","\\*/",{sL:"markdown"}),e.C("///","$",{sL:"markdown"}),e.CLCM,e.CBCM,{cN:"class",bK:"class interface",e:"{",eE:!0,c:[{bK:"extends implements"},e.UTM]},e.CNM,{cN:"meta",b:"@[A-Za-z]+"},{b:"=>"}]}}); -------------------------------------------------------------------------------- /code.js: -------------------------------------------------------------------------------- 1 | 2 | $(function () { 3 | //初始化 4 | (function init() { 5 | 6 | function showInfo(info) { 7 | $('.info').show().html(info); 8 | } 9 | function hideInfo() { 10 | $('.info').hide(); 11 | } 12 | const jsonEditorCachekey = 'jsonEditor'; 13 | 14 | let resultDartCode = ''; 15 | 16 | let jsonTestCase = { 17 | "some_snake_case_prop": "", 18 | "anInt": 1, 19 | "aDouble": 2.3, 20 | "aString": "hello", 21 | "aBool": false, 22 | "anObj": { 23 | "name": "x", 24 | "age": 18.1 25 | }, 26 | "anObjList": [ 27 | { 28 | "name": "y" 29 | } 30 | ], 31 | "aStrList": [ 32 | "something" 33 | ], 34 | "multidimensional": [ 35 | [ 36 | [ 37 | { 38 | "name": "y" 39 | } 40 | ] 41 | ] 42 | ] 43 | }; 44 | 45 | // create the editor 46 | const container = document.getElementById("origJsonContainer") 47 | const options = { 48 | "mode": "code", 49 | onChangeText: (str) => { 50 | $.cookie(jsonEditorCachekey, str); 51 | generate(); 52 | }, 53 | } 54 | let editor; 55 | try { 56 | editor = new JSONEditor(container, options) 57 | } catch { 58 | showInfo('Load JSONEditor faild, please try reload'); 59 | } 60 | 61 | function tryParseJSON(jsonString) { 62 | try { 63 | var o = JSON.parse(jsonString); 64 | if (o && typeof o === "object") { 65 | return o; 66 | } 67 | } 68 | catch (e) { } 69 | return false; 70 | } 71 | 72 | function generate() { 73 | hideInfo(); 74 | let jsonObj; 75 | try { 76 | jsonObj = editor.get(); 77 | } catch (error) { 78 | $('#dartCode').html(error.toString()); 79 | return; 80 | } 81 | 82 | let forceStringCheckBox = $('#forceStringCheckBox').prop('checked'); 83 | let shouldEnhanceFaultTolerance = $('#faultToleranceCheckBox').prop('checked'); 84 | 85 | //snake to camel 86 | const snakeToCamel = (str) => str.replace( 87 | /([-_][a-zA-Z])/g, 88 | (group) => group.toUpperCase() 89 | .replace('-', '') 90 | .replace('_', '') 91 | ); 92 | 93 | //去除重复元素 94 | let removeSurplusElement = (obj) => { 95 | if (Array.isArray(obj)) { 96 | obj.length = 1; 97 | removeSurplusElement(obj[0]); 98 | } 99 | else if (typeof obj === 'object') { 100 | for (let key in obj) { 101 | if (obj.hasOwnProperty(key)) { 102 | removeSurplusElement(obj[key]) 103 | } 104 | } 105 | } 106 | }; 107 | //大写转换 108 | let uppercaseFirst = (string) => { 109 | return string.charAt(0).toUpperCase() + string.slice(1); 110 | }; 111 | //Dart关键字保护 112 | let dartKeywordDefence = key => { 113 | if (typeof key === 'string') { 114 | //https://dart.dev/guides/language/language-tour 115 | let reservedKeywords = ["num", "double", "int", "String", "bool", "List", "abstract", "dynamic", "implements", "show", "as", "else", "import", "static", "assert", "enum", "in", "super", "async", "export", "interface", "switch", "await", "extends", "is", "sync", "break", "external", "library", "this", "case", "factory", "mixin", "throw", "catch", "false", "new", "true", "class", "final", "null", "try", "const", "finally", "on", "typedef", "continue", "for", "operator", "var", "covariant", "Function", "part", "void", "default", "get", "rethrow", "while", "deferred", "hide", "return", "with", "do", "if", "set", "yield"]; 116 | let isStartWithNum = key.match(/^\d/); 117 | if (reservedKeywords.includes(key) || isStartWithNum) { 118 | return `the${uppercaseFirst(key)}`; 119 | } 120 | } 121 | return key; 122 | }; 123 | 124 | //泛型字符串生成器 125 | let genericStringGenerator = (innerClass, count) => { 126 | let genericStrings = [innerClass]; 127 | while (count) { 128 | genericStrings.unshift('List<'); 129 | genericStrings.push('>'); 130 | count--; 131 | } 132 | let genericString = genericStrings.join(''); 133 | return genericString; 134 | } 135 | 136 | //!获取最内层对象,类型和层数 137 | let getInnerObjInfo = (arr, className) => { 138 | let count = 0; 139 | let getInnerObj = (arr) => { 140 | if (Array.isArray(arr)) { 141 | let first = arr[0]; 142 | count++; 143 | return getInnerObj(first); 144 | } 145 | else { 146 | return arr; 147 | } 148 | } 149 | 150 | let inner = getInnerObj(arr); 151 | let innerClass = className; 152 | if (typeof inner === 'object') { 153 | } 154 | else if (typeof inner === 'boolean') { 155 | //we don't handle boolean 156 | innerClass = 'bool'; 157 | } 158 | else { 159 | if (typeof inner === 'string') { 160 | innerClass = 'String'; 161 | } 162 | if (typeof inner === 'number') { 163 | if (Number.isInteger(inner)) { 164 | innerClass = 'int'; 165 | 166 | } else { 167 | innerClass = 'double'; 168 | } 169 | } 170 | if (forceStringCheckBox) { 171 | innerClass = 'String'; 172 | } 173 | } 174 | return { inner, innerClass, count }; 175 | }; 176 | //!获取数组循环语句 177 | let getIterateLines = (arr, className, key, legalKey, jsonKey, shouldNullSafe) => { 178 | 179 | if (legalKey == 'data') { 180 | legalKey = 'this.data'; 181 | } 182 | 183 | function makeBlank(count) { 184 | let str = ''; 185 | for (let index = 0; index < count + 1; index++) { 186 | str += ' '; 187 | } 188 | return str; 189 | }; 190 | 191 | let { inner, innerClass, count } = getInnerObjInfo(arr, className); 192 | if (inner === undefined || inner === null) { 193 | showInfo(`WARNING : the property named   '${key}'   is an EMPTY array ! parse process is failed !`); 194 | return { fromJsonLinesJoined: " >>>>>>error<<<<<<\n", toJsonLinesJoined: " >>>>>>error<<<<<<\n" }; 195 | } 196 | let total = count; 197 | let fromJsonLines = []; 198 | let toJsonLines = []; 199 | 200 | count--; 201 | 202 | if (typeof inner === 'object') { 203 | fromJsonLines.push(`${makeBlank(count * 3)}v.forEach((v) {\n${makeBlank(count * 4)}arr${count}.add(${className}.fromJson(v));\n${makeBlank(count * 3)}});`); 204 | toJsonLines.push(`${makeBlank(count * 3)}v${shouldNullSafe ? '!' : ''}.forEach((v) {\n${makeBlank(count * 4)}arr${count}.add(v${shouldNullSafe ? '!' : ''}.toJson());\n${makeBlank(count * 3)}});`); 205 | } else { 206 | let toType = 'v'; 207 | if (typeof inner === 'boolean') { 208 | //we don't handle boolean 209 | } 210 | else { 211 | if (forceStringCheckBox) { 212 | inner = inner.toString(); 213 | } 214 | if (typeof inner === 'string') { 215 | toType = 'v.toString()'; 216 | } 217 | if (typeof inner === 'number') { 218 | if (Number.isInteger(inner)) { 219 | toType = shouldEnhanceFaultTolerance ? 'int.tryParse(v.toString() ?? \'\')' : 'v.toInt()'; 220 | } else { 221 | toType = shouldEnhanceFaultTolerance ? 'double.tryParse(v.toString() ?? \'\')' : 'v.toDouble()'; 222 | } 223 | } 224 | } 225 | if ((typeof inner === 'string') || (typeof inner === 'number') || (typeof inner === 'boolean')) { 226 | fromJsonLines.push(`${makeBlank(count * 3)}v.forEach((v) {\n${makeBlank(count * 4)}arr${count}.add(${toType});\n${makeBlank(count * 3)}});`); 227 | toJsonLines.push(`${makeBlank(count * 3)}v${shouldNullSafe ? '!' : ''}.forEach((v) {\n${makeBlank(count * 4)}arr${count}.add(v);\n${makeBlank(count * 3)}});`); 228 | } 229 | } 230 | 231 | while (count) { 232 | fromJsonLines.unshift(`${makeBlank(count * 2)}v.forEach((v) {\n${makeBlank(count * 3)}final arr${count} = ${genericStringGenerator(innerClass, total - count).slice(4)}[];`); 233 | fromJsonLines.push(`${makeBlank(count * 3)}arr${count - 1}.add(arr${count});\n${makeBlank(count * 2)}});`); 234 | toJsonLines.unshift(`${makeBlank(count * 2)}v${shouldNullSafe ? '!' : ''}.forEach((v) {\n${makeBlank(count * 3)}final arr${count} = [];`); 235 | toJsonLines.push(`${makeBlank(count * 3)}arr${count - 1}.add(arr${count});\n${makeBlank(count * 2)}});`); 236 | count--; 237 | } 238 | 239 | let typeCheck = shouldEnhanceFaultTolerance ? ` && (json[${jsonKey}] is List)` : ''; 240 | fromJsonLines.unshift(`${makeBlank(count * 2)}if (json[${jsonKey}] != null${typeCheck}) {\n${makeBlank(count * 2)}final v = json[${jsonKey}];\n${makeBlank(count * 2)}final arr0 = ${genericStringGenerator(innerClass, total).slice(4)}[];`); 241 | fromJsonLines.push(`${makeBlank(count * 2)}${makeBlank(count)}${legalKey} = arr0;\n }\n`); 242 | toJsonLines.unshift(` if (${legalKey} != null) {\n final v = ${legalKey};\n final arr0 = [];`); 243 | toJsonLines.push(` data[${jsonKey}] = arr0;\n }\n`); 244 | 245 | let fromJsonLinesJoined = fromJsonLines.join('\r\n'); 246 | let toJsonLinesJoined = toJsonLines.join('\r\n'); 247 | return { fromJsonLinesJoined, toJsonLinesJoined }; 248 | }; 249 | 250 | //!json对象转dart 251 | let objToDart = (jsonObj, prefix, baseClass) => { 252 | 253 | if (Array.isArray(jsonObj)) { 254 | return objToDart(jsonObj[0], prefix, baseClass); 255 | } 256 | 257 | let lines = []; 258 | 259 | let jsonKeysLines = []; 260 | 261 | let propsLines = []; 262 | let constructorLines = []; 263 | let fromJsonLines = []; 264 | let toJsonLines = []; 265 | 266 | let shouldUsingJsonKey = $('#usingJsonKeyCheckBox').prop('checked'); 267 | let shouldNullSafe = $('#nullSafeCheckBox').prop('checked'); 268 | let isJsonKeyPrivate = $('#jsonKeyPrivateCheckBox').prop('checked'); 269 | let shouldConvertSnakeToCamel = $('#camelCheckBox').prop('checked'); 270 | let shouldEnhanceFaultTolerance = $('#faultToleranceCheckBox').prop('checked'); 271 | let shouldOridJson = $('#origJsonCheckBox').prop('checked'); 272 | 273 | let className = `${prefix}${uppercaseFirst(baseClass)}`; 274 | if (shouldConvertSnakeToCamel) { 275 | className = snakeToCamel(className); 276 | } 277 | 278 | lines.push(`class ${className} {`); 279 | lines.push(`/*\r\n${JSON.stringify(jsonObj, null, 2)} \r\n*/\r\n`); 280 | 281 | constructorLines.push(` ${className}({\n`); 282 | fromJsonLines.push(` ${className}.fromJson(Map json) {\n`); 283 | if (shouldOridJson) { 284 | fromJsonLines.push(` __origJson = json;\n`); 285 | } 286 | toJsonLines.push(` Map toJson() {\n`); 287 | toJsonLines.push(` final data = {};\n`); 288 | 289 | for (let key in jsonObj) { 290 | if (jsonObj.hasOwnProperty(key)) { 291 | let element = jsonObj[key]; 292 | 293 | let legalKey = dartKeywordDefence(key); 294 | 295 | if (shouldConvertSnakeToCamel) { 296 | legalKey = snakeToCamel(legalKey); 297 | } 298 | 299 | let thisData = ''; 300 | if (key == 'data') { 301 | thisData = 'this.'; 302 | } 303 | 304 | let jsonKey = `'${key}'`; 305 | if (shouldUsingJsonKey) { 306 | jsonKey = `${isJsonKeyPrivate ? '_' : ''}jsonKey${className}${uppercaseFirst(legalKey)}`; 307 | } 308 | jsonKeysLines.push(`const String ${jsonKey} = '${key}';`); 309 | constructorLines.push(` this.${legalKey},\n`); 310 | if (element === null) { 311 | //!显示错误信息 312 | showInfo(`WARNING : the Property named '${key}' is null,which will be treated as String type`); 313 | element = ''; 314 | } 315 | if (typeof element === 'object') { 316 | 317 | let subClassName = `${className}${uppercaseFirst(key)}`; 318 | if (shouldConvertSnakeToCamel) { 319 | subClassName = snakeToCamel(subClassName); 320 | } 321 | if (Array.isArray(element)) { 322 | let { inner, innerClass, count } = getInnerObjInfo(element, subClassName); 323 | let { fromJsonLinesJoined, toJsonLinesJoined } = getIterateLines(element, subClassName, key, legalKey, jsonKey, shouldNullSafe); 324 | let genericString = genericStringGenerator(innerClass, count); 325 | if (shouldNullSafe) { 326 | genericString = genericString.replaceAll('>', '?>') + '?'; 327 | } 328 | propsLines.push(` ${genericString} ${legalKey};\n`); 329 | fromJsonLines.push(fromJsonLinesJoined); 330 | toJsonLines.push(toJsonLinesJoined); 331 | if (typeof inner === 'object') { 332 | lines.unshift(objToDart(element, className, key)); 333 | } 334 | } 335 | else { 336 | 337 | lines.unshift(objToDart(element, className, key)); 338 | propsLines.push(` ${subClassName}${shouldNullSafe ? '?' : ''} ${legalKey};\n`); 339 | let typeCheck = shouldEnhanceFaultTolerance ? ` && (json[${jsonKey}] is Map)` : ''; 340 | fromJsonLines.push(` ${legalKey} = (json[${jsonKey}] != null${typeCheck}) ? ${subClassName}.fromJson(json[${jsonKey}]) : null;\n`); 341 | toJsonLines.push(` if (${legalKey} != null) {\n data[${jsonKey}] = ${thisData}${legalKey}${shouldNullSafe ? '!' : ''}.toJson();\n }\n`); 342 | } 343 | } 344 | else { 345 | let toType = `json[${jsonKey}]`; 346 | let type = ''; 347 | if (typeof element === 'boolean') { 348 | //bool is special 349 | type = 'bool'; 350 | } 351 | else { 352 | if (forceStringCheckBox) { 353 | element = element.toString(); 354 | } 355 | if (typeof element === 'string') { 356 | toType = `json[${jsonKey}]?.toString()`; 357 | type = 'String'; 358 | } 359 | else if (typeof element === 'number') { 360 | if (Number.isInteger(element)) { 361 | toType = shouldEnhanceFaultTolerance ? `int.tryParse(json[${jsonKey}]?.toString() ?? '')` : `json[${jsonKey}]?.toInt()`; 362 | type = 'int'; 363 | } else { 364 | toType = shouldEnhanceFaultTolerance ? `double.tryParse(json[${jsonKey}]?.toString() ?? '')` : `json[${jsonKey}]?.toDouble()`; 365 | type = 'double'; 366 | } 367 | } 368 | } 369 | propsLines.push(` ${type}${shouldNullSafe ? '?' : ''} ${legalKey};\n`); 370 | fromJsonLines.push(` ${legalKey} = ${toType};\n`); 371 | toJsonLines.push(` data[${jsonKey}] = ${thisData}${legalKey};\n`); 372 | } 373 | } 374 | } 375 | if (shouldOridJson) { 376 | propsLines.push(` Map __origJson = {};\n`); 377 | } 378 | if (shouldUsingJsonKey) { 379 | lines.unshift(jsonKeysLines.join('\n')); 380 | } 381 | 382 | constructorLines.push(` });`); 383 | fromJsonLines.push(` }`); 384 | toJsonLines.push(` return data;\n }`); 385 | 386 | lines.push(propsLines.join('')); 387 | lines.push(constructorLines.join('')); 388 | lines.push(fromJsonLines.join('')); 389 | lines.push(toJsonLines.join('')); 390 | if (shouldOridJson) { 391 | lines.push(` Map origJson() => __origJson;`); 392 | } 393 | 394 | lines.push(`}\n`); 395 | 396 | let linesOutput = lines.join('\r\n'); 397 | 398 | return linesOutput; 399 | }; 400 | 401 | removeSurplusElement(jsonObj); 402 | 403 | let rootClass = $('#classNameTextField').val(); 404 | let dartCode = `///\n/// Code generated by jsonToDartModel https://ashamp.github.io/jsonToDartModel/\n///\n${objToDart(jsonObj, rootClass, "")}`; 405 | 406 | resultDartCode = dartCode; 407 | let highlightDartCode = hljs.highlight('dart', dartCode); 408 | $('#dartCode').html(highlightDartCode.value); 409 | $('#fileNameTextField').val(rootClass.length > 0 ? rootClass.replace(/([A-Z])/g, "_$1").toLowerCase().substr(1) + '.dart' : ''); 410 | } 411 | 412 | function textFieldBinding(tfID, defaultValue) { 413 | let selector = '#' + tfID; 414 | let strFromCookie = $.cookie(tfID); 415 | if ((strFromCookie === undefined || strFromCookie.length === 0) && defaultValue) { 416 | $.cookie(tfID, defaultValue); 417 | } 418 | $(selector).val($.cookie(tfID)); 419 | $(selector).on('input', function (e) { 420 | let text = $(this).val(); 421 | $.cookie(tfID, text); 422 | generate(); 423 | }); 424 | } 425 | 426 | //textFieldBinding('origJsonTextarea', jsonTestCase); 427 | textFieldBinding('classNameTextField', 'SomeRootEntity'); 428 | 429 | function jsonEditorBinding(tfID, defaultValue) { 430 | let str = $.cookie(jsonEditorCachekey); 431 | if (str && str.length) { 432 | editor.setText(str); 433 | } else { 434 | editor.set(jsonTestCase); 435 | } 436 | } 437 | jsonEditorBinding(); 438 | 439 | function checkBoxBinding(checkBoxID, checked) { 440 | let defaultValue = checked ? '1' : '0'; 441 | let selector = '#' + checkBoxID; 442 | let strFromCookie = $.cookie(checkBoxID); 443 | if (strFromCookie === undefined || strFromCookie.length === 0) { 444 | $.cookie(checkBoxID, defaultValue); 445 | } 446 | checked = $.cookie(checkBoxID) === '1'; 447 | $(selector).prop('checked', checked); 448 | $(selector).on('change', function () { 449 | let checked = $(this).prop('checked') ? '1' : '0'; 450 | $.cookie(checkBoxID, checked); 451 | generate(); 452 | }); 453 | } 454 | 455 | checkBoxBinding('jsonKeyPrivateCheckBox', true); 456 | checkBoxBinding('usingJsonKeyCheckBox', false); 457 | checkBoxBinding('nullSafeCheckBox', false); 458 | checkBoxBinding('camelCheckBox', true); 459 | checkBoxBinding('faultToleranceCheckBox', false); 460 | checkBoxBinding('forceStringCheckBox', false); 461 | checkBoxBinding('origJsonCheckBox', false); 462 | 463 | $('#usingJsonKeyCheckBox').on('change', function () { 464 | $('#jsonKeyPrivateCheckBox').prop('disabled', !(this.checked)); 465 | }); 466 | $('#jsonKeyPrivateCheckBox').prop('disabled', !($('#usingJsonKeyCheckBox').prop('checked'))); 467 | 468 | generate(); 469 | 470 | function copyToClipboard(text) { 471 | var $temp = $("