Appearance
实现 JSON 转类型定义 工具
分类:developer | 标签:JSON、类型、interface JSON 转 TypeScript/Java/Go/Dart/C# 类型定义
2.1 功能说明
JSON 转 TypeScript/Java/Go/Dart/C# 类型定义
2.1.1 使用指南
功能说明
根据 JSON 样例生成 TypeScript interface、Java POJO、Go struct、Dart class、C# Model,自动推断嵌套结构与可选字段。
使用场景
接口联调、Mock 数据建模、前端类型定义快速生成。
2.2 代码实现
2.2.1 组件结构
本工具是一个基于 Vue 3 <script setup> 语法的单文件组件(SFC),统一包裹在 ToolPageShell 组件内,由它提供页面标题、工具 ID、分类与「使用指南」插槽等通用外壳;核心业务逻辑(响应式状态、计算属性、事件处理函数)全部写在 <script setup> 中,输入/输出通过 el-input、el-button 等 Element Plus 组件与用户交互,所有数据均在浏览器本地处理,不会上传服务器。
2.2.2 核心逻辑概览
<script setup> 中定义的主要函数/方法:
pascal()singular()resolve()buildInterface()numType()run()copy()download()ext()
2.2.3 关键实现代码
以下是该工具的完整 <script setup> 实现(核心代码)。模板(<template>)与样式(<style>)已省略。
vue
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import ToolPageShell from '@/components/ToolPageShell.vue'
const langs = ['TypeScript', 'Java', 'Go', 'Dart', 'C#']
const lang = ref('TypeScript')
const rootName = ref('Root')
const json = ref('')
const out = ref('')
const err = ref('')
function pascal(s) {
const r = String(s).replace(/(^|[^a-zA-Z0-9])([a-zA-Z0-9])/g, (_, __, c) => c.toUpperCase()).replace(/[^a-zA-Z0-9]/g, '')
return r || 'Root'
}
function singular(s) {
const p = pascal(s)
if (p.length > 1 && p.endsWith('s')) return p.slice(0, -1)
return p || 'Item'
}
let interfaces // name -> { fields:[{key,optional,type}] }
let order
function resolve(samples, name) {
const nonNull = samples.filter(v => v !== null && v !== undefined)
if (nonNull.length === 0) return 'any'
const first = nonNull[0]
if (Array.isArray(first)) {
const elems = nonNull.filter(Array.isArray).map(a => a[0]).filter(v => v !== undefined)
return (elems.length ? resolve(elems, singular(name)) : 'any') + '[]'
}
if (typeof first === 'object') {
return buildInterface(nonNull.filter(v => v && typeof v === 'object' && !Array.isArray(v)), name)
}
if (typeof first === 'number') return nonNull.every(v => Number.isInteger(v)) ? 'int' : 'float'
if (typeof first === 'boolean') return 'boolean'
if (typeof first === 'string') return 'string'
return 'any'
}
function buildInterface(objs, name) {
const ifaceName = pascal(name) || 'Root'
if (interfaces[ifaceName]) return ifaceName
interfaces[ifaceName] = null
order.push(ifaceName)
const keyInfo = new Map()
for (const o of objs) for (const k of Object.keys(o)) if (!keyInfo.has(k)) keyInfo.set(k, { present: 0 })
const childSamples = {}
for (const k of keyInfo.keys()) {
childSamples[k] = objs.map(o => (o && typeof o === 'object' && k in o) ? o[k] : undefined).filter(v => v !== undefined)
}
const fields = []
for (const k of keyInfo.keys()) {
const info = keyInfo.get(k)
fields.push({ key: k, optional: info.present < objs.length, type: resolve(childSamples[k], pascal(k)) })
}
interfaces[ifaceName] = fields
return ifaceName
}
function numType(t, l) {
if (t === 'int') return { TypeScript: 'number', Java: 'int', Go: 'int', Dart: 'int', 'C#': 'int' }[l]
if (t === 'float') return { TypeScript: 'number', Java: 'double', Go: 'float64', Dart: 'double', 'C#': 'double' }[l]
return t
}
function run() {
err.value = ''; out.value = ''
const raw = json.value.trim()
if (!raw) { err.value = '请输入 JSON'; return }
let data
try { data = JSON.parse(raw) } catch (e) { err.value = 'JSON 解析失败:' + e.message; return }
interfaces = {}; order = []
const rootType = Array.isArray(data) ? resolve(data, rootName.value) : resolve([data], rootName.value)
const l = lang.value
const lines = []
for (const name of order) {
const fields = interfaces[name]
if (l === 'TypeScript') {
lines.push(`interface ${name} {`)
for (const f of fields) lines.push(` ${f.key}${f.optional ? '?' : ''}: ${numType(f.type, l)};`)
lines.push('}')
} else if (l === 'Java') {
lines.push(`public class ${name} {`)
for (const f of fields) lines.push(` private ${numType(f.type, l)} ${f.key};`)
lines.push('}')
} else if (l === 'Go') {
lines.push(`type ${name} struct {`)
for (const f of fields) lines.push(` ${pascal(f.key)} ${numType(f.type, l)} \`json:"${f.key}${f.optional ? ',omitempty' : ''}"\``)
lines.push('}')
} else if (l === 'Dart') {
lines.push(`class ${name} {`)
for (const f of fields) lines.push(` ${numType(f.type, l)}${f.optional ? '?' : ''} ${f.key};`)
lines.push(` ${name}({${fields.map(f => `this.${f.key}`).join(', ')}});`)
lines.push('}')
} else if (l === 'C#') {
lines.push(`public class ${name} {`)
for (const f of fields) lines.push(` public ${numType(f.type, l)} ${pascal(f.key)} { get; set; }`)
lines.push('}')
}
lines.push('')
}
out.value = lines.join('\n').trimEnd()
if (lang.value === 'TypeScript' || lang.value === 'Go') out.value = `// 由 JSON 样例生成(根类型:${rootType.replace('[]', '')})\n` + out.value
}
function copy() { if (out.value) navigator.clipboard?.writeText(out.value).then(() => ElMessage.success('已复制')) }
function download() {
if (!out.value) return
const blob = new Blob([out.value], { type: 'text/plain;charset=utf-8' })
const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `${rootName.value || 'model'}.${ext()}`
a.click(); URL.revokeObjectURL(a.href)
}
function ext() { return { TypeScript: 'ts', Java: 'java', Go: 'go', Dart: 'dart', 'C#': 'cs' }[lang.value] }2.3 效果截图
