Skip to content

实现 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-inputel-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 效果截图

JSON 转类型定义 效果截图

工具访问地址:https://www.i91tools.com/tools/json-to-type