Ruby JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,广泛用于Web服务、API通信和配置文件。Ruby提供了内置的JSON库,使得JSON数据的解析和生成变得简单直观。本章将详细介绍如何在Ruby中处理JSON数据,包括解析、生成、验证和最佳实践。
🎯 JSON基础
什么是JSON
JSON(JavaScript Object Notation)是一种基于文本的开放标准数据交换格式。它具有以下特点:
- 轻量级: 相比XML更简洁
- 易读: 人类可读的文本格式
- 语言无关: 支持多种编程语言
- 结构化: 支持对象、数组等复杂数据结构
JSON语法
json
{
"name": "张三",
"age": 25,
"isStudent": true,
"courses": ["数学", "英语", "计算机"],
"address": {
"city": "北京",
"district": "朝阳区"
},
"score": null
}📖 JSON解析
基本解析
ruby
require 'json'
# 解析JSON字符串
json_string = '{"name": "张三", "age": 25, "city": "北京"}'
parsed_data = JSON.parse(json_string)
puts parsed_data['name'] # 张三
puts parsed_data['age'] # 25
puts parsed_data['city'] # 北京
# 解析JSON数组
json_array = '[1, 2, 3, 4, 5]'
numbers = JSON.parse(json_array)
puts numbers.inspect # [1, 2, 3, 4, 5]
# 解析复杂JSON结构
complex_json = <<~JSON
{
"users": [
{
"id": 1,
"name": "张三",
"email": "zhangsan@example.com",
"profile": {
"age": 25,
"city": "北京"
}
},
{
"id": 2,
"name": "李四",
"email": "lisi@example.com",
"profile": {
"age": 30,
"city": "上海"
}
}
],
"total": 2
}
JSON
data = JSON.parse(complex_json)
puts "用户总数: #{data['total']}"
puts "第一个用户: #{data['users'][0]['name']}"
puts "第二个用户年龄: #{data['users'][1]['profile']['age']}"符号化键解析
ruby
require 'json'
# 使用符号作为键
json_string = '{"name": "张三", "age": 25, "city": "北京"}'
# 默认使用字符串键
default_parsed = JSON.parse(json_string)
puts default_parsed.keys # ["name", "age", "city"]
# 使用符号键
symbolized_parsed = JSON.parse(json_string, symbolize_names: true)
puts symbolized_parsed.keys # [:name, :age, :city]
puts symbolized_parsed[:name] # 张三
# 创建自定义对象
class Person
attr_accessor :name, :age, :city
def initialize(name, age, city)
@name = name
@age = age
@city = city
end
def self.from_json(json_string)
data = JSON.parse(json_string, symbolize_names: true)
new(data[:name], data[:age], data[:city])
end
end
# 使用自定义对象
person_json = '{"name": "王五", "age": 28, "city": "广州"}'
person = Person.from_json(person_json)
puts "姓名: #{person.name}, 年龄: #{person.age}, 城市: #{person.city}"错误处理
ruby
require 'json'
# 安全解析JSON
def safe_json_parse(json_string)
begin
JSON.parse(json_string)
rescue JSON::ParserError => e
puts "JSON解析错误: #{e.message}"
nil
end
end
# 有效的JSON
valid_json = '{"name": "张三", "age": 25}'
result = safe_json_parse(valid_json)
puts result.inspect unless result.nil?
# 无效的JSON
invalid_json = '{"name": "张三", "age": 25' # 缺少结束括号
result = safe_json_parse(invalid_json)
puts "解析结果: #{result}" # 解析结果:
# 处理特殊字符
json_with_unicode = '{"message": "你好,世界!"}'
parsed = JSON.parse(json_with_unicode)
puts parsed['message'] # 你好,世界!🎨 JSON生成
基本生成
ruby
require 'json'
# 从哈希生成JSON
data = {
name: "张三",
age: 25,
city: "北京",
hobbies: ["读书", "游泳", "编程"]
}
json_string = JSON.generate(data)
puts json_string
# {"name":"张三","age":25,"city":"北京","hobbies":["读书","游泳","编程"]}
# 格式化输出
pretty_json = JSON.pretty_generate(data)
puts pretty_json
# {
# "name": "张三",
# "age": 25,
# "city": "北京",
# "hobbies": [
# "读书",
# "游泳",
# "编程"
# ]
# }
# 从数组生成JSON
numbers = [1, 2, 3, 4, 5]
json_array = JSON.generate(numbers)
puts json_array # [1,2,3,4,5]自定义对象序列化
ruby
require 'json'
# 为自定义类添加JSON序列化支持
class User
attr_accessor :id, :name, :email, :created_at
def initialize(id, name, email)
@id = id
@name = name
@email = email
@created_at = Time.now
end
# 定义如何转换为JSON
def to_json(*args)
{
id: @id,
name: @name,
email: @email,
created_at: @created_at.iso8601
}.to_json(*args)
end
# 从JSON创建对象
def self.from_json(json_string)
data = JSON.parse(json_string, symbolize_names: true)
user = new(data[:id], data[:name], data[:email])
user.created_at = Time.parse(data[:created_at])
user
end
end
# 使用自定义序列化
user = User.new(1, "张三", "zhangsan@example.com")
json_output = user.to_json
puts json_output
# 从JSON恢复对象
restored_user = User.from_json(json_output)
puts "恢复的用户: #{restored_user.name}, 创建时间: #{restored_user.created_at}"控制JSON输出格式
ruby
require 'json'
# 控制JSON生成选项
data = {
name: "张三",
age: 25,
bio: "这是一个很长的个人简介,包含了很多信息。",
scores: [95, 87, 92, 88, 90]
}
# 标准生成
puts "=== 标准生成 ==="
puts JSON.generate(data)
# 美化生成
puts "\n=== 美化生成 ==="
puts JSON.pretty_generate(data)
# 自定义缩进
puts "\n=== 自定义缩进 ==="
puts JSON.pretty_generate(data, indent: ' ')
# 限制深度
deep_data = {
level1: {
level2: {
level3: {
level4: "深层数据"
}
}
}
}
puts "\n=== 限制深度 ==="
puts JSON.pretty_generate(deep_data, max_nesting: 3)
# ASCII编码(避免Unicode转义)
chinese_data = {
name: "张三",
city: "北京"
}
puts "\n=== ASCII编码 ==="
puts JSON.generate(chinese_data, ascii_only: true)
puts "\n=== UTF-8编码 ==="
puts JSON.generate(chinese_data)🔄 JSON与其它数据格式
JSON与YAML转换
ruby
require 'json'
require 'yaml'
# JSON转YAML
json_data = '{"name": "张三", "age": 25, "hobbies": ["读书", "游泳"]}'
parsed_data = JSON.parse(json_data)
yaml_output = YAML.dump(parsed_data)
puts "YAML格式:"
puts yaml_output
# YAML转JSON
yaml_data = <<~YAML
---
name: 李四
age: 30
hobbies:
- 旅游
- 摄影
YAML
yaml_parsed = YAML.load(yaml_data)
json_output = JSON.generate(yaml_parsed)
puts "\nJSON格式:"
puts json_outputJSON与CSV转换
ruby
require 'json'
require 'csv'
# JSON数组转CSV
json_array = <<~JSON
[
{"name": "张三", "age": 25, "city": "北京"},
{"name": "李四", "age": 30, "city": "上海"},
{"name": "王五", "age": 28, "city": "广州"}
]
JSON
data = JSON.parse(json_array)
# 生成CSV
csv_string = CSV.generate do |csv|
# 写入标题行
csv << data.first.keys
# 写入数据行
data.each do |row|
csv << row.values
end
end
puts "CSV格式:"
puts csv_string
# CSV转JSON
csv_data = <<~CSV
name,age,city
张三,25,北京
李四,30,上海
王五,28,广州
CSV
csv_parsed = CSV.parse(csv_data, headers: true)
json_output = JSON.pretty_generate(csv_parsed.map(&:to_hash))
puts "\nJSON格式:"
puts json_output🎯 JSON实用示例
API客户端处理
ruby
require 'json'
require 'net/http'
require 'uri'
class APIClient
def initialize(base_url)
@base_url = base_url
end
def get(path)
uri = URI.join(@base_url, path)
response = Net::HTTP.get_response(uri)
if response.code == '200'
JSON.parse(response.body)
else
{ error: "HTTP #{response.code}", message: response.message }
end
end
def post(path, data)
uri = URI.join(@base_url, path)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = uri.scheme == 'https'
request = Net::HTTP::Post.new(uri)
request['Content-Type'] = 'application/json'
request.body = JSON.generate(data)
response = http.request(request)
if response.code.start_with?('2')
JSON.parse(response.body)
else
{ error: "HTTP #{response.code}", message: response.message }
end
end
end
# 使用API客户端
# client = APIClient.new('https://api.example.com/')
#
# # GET请求
# users = client.get('/api/users')
# puts JSON.pretty_generate(users)
#
# # POST请求
# new_user = client.post('/api/users', { name: '新用户', email: 'new@example.com' })
# puts JSON.pretty_generate(new_user)配置文件处理
ruby
require 'json'
class ConfigManager
def initialize(config_file)
@config_file = config_file
@config = load_config
end
def [](key)
@config[key]
end
def []=(key, value)
@config[key] = value
save_config
end
def get(key, default = nil)
@config.fetch(key, default)
end
def set(key, value)
@config[key] = value
save_config
end
def reload
@config = load_config
end
private
def load_config
if File.exist?(@config_file)
JSON.parse(File.read(@config_file))
else
{}
end
rescue JSON::ParserError => e
puts "配置文件解析错误: #{e.message}"
{}
end
def save_config
File.write(@config_file, JSON.pretty_generate(@config))
end
end
# 使用配置管理器
# 创建示例配置文件
sample_config = {
"database" => {
"host" => "localhost",
"port" => 5432,
"name" => "myapp_db"
},
"logging" => {
"level" => "INFO",
"file" => "/var/log/myapp.log"
},
"features" => {
"user_management" => true,
"reporting" => false
}
}
File.write('app_config.json', JSON.pretty_generate(sample_config))
# 使用配置管理器
config = ConfigManager.new('app_config.json')
puts "数据库主机: #{config['database']['host']}"
puts "日志级别: #{config.get('logging', {})['level']}"
puts "用户管理功能: #{config.get('features', {})['user_management']}"
# 更新配置
config.set('features.user_management', false)
config['logging']['level'] = 'DEBUG'
# 重新加载配置
config.reload
puts "更新后的日志级别: #{config['logging']['level']}"数据验证和清理
ruby
require 'json'
class JSONValidator
# 验证JSON结构
def self.validate_structure(data, schema)
case schema
when Hash
validate_hash(data, schema)
when Array
validate_array(data, schema)
else
data.is_a?(schema)
end
end
# 清理JSON数据
def self.sanitize(data)
case data
when Hash
data.each_with_object({}) do |(key, value), result|
result[key.to_s] = sanitize(value)
end
when Array
data.map { |item| sanitize(item) }
when String
data.strip
else
data
end
end
# 深度合并JSON对象
def self.deep_merge(hash1, hash2)
merged = hash1.dup
hash2.each do |key, value|
merged[key] = if merged[key].is_a?(Hash) && value.is_a?(Hash)
deep_merge(merged[key], value)
else
value
end
end
merged
end
private
def self.validate_hash(data, schema)
return false unless data.is_a?(Hash)
schema.all? do |key, value_schema|
if key.to_s.end_with?('?')
# 可选字段
optional_key = key.to_s.chomp('?').to_sym
!data.key?(optional_key) || validate_structure(data[optional_key], value_schema)
else
# 必需字段
data.key?(key) && validate_structure(data[key], value_schema)
end
end
end
def self.validate_array(data, schema)
return false unless data.is_a?(Array)
return true if schema.empty?
item_schema = schema.first
data.all? { |item| validate_structure(item, item_schema) }
end
end
# 使用验证器
user_schema = {
name: String,
age: Integer,
email: String,
"address?" => {
street: String,
city: String
}
}
valid_user = {
name: "张三",
age: 25,
email: "zhangsan@example.com",
address: {
street: "长安街1号",
city: "北京"
}
}
invalid_user = {
name: "李四",
age: "不是数字",
email: "lisi@example.com"
}
puts "有效用户验证: #{JSONValidator.validate_structure(valid_user, user_schema)}"
puts "无效用户验证: #{JSONValidator.validate_structure(invalid_user, user_schema)}"
# 使用数据清理
dirty_data = {
name: " 张三 ",
age: 25,
hobbies: [" 读书 ", " 游泳 ", " 编程 "],
address: {
city: " 北京 "
}
}
clean_data = JSONValidator.sanitize(dirty_data)
puts "清理后的数据:"
puts JSON.pretty_generate(clean_data)
# 深度合并
default_config = {
database: {
host: "localhost",
port: 5432,
pool: 5
},
logging: {
level: "INFO",
format: "json"
}
}
user_config = {
database: {
host: "production.db.com",
pool: 10
},
features: {
caching: true
}
}
merged_config = JSONValidator.deep_merge(default_config, user_config)
puts "合并后的配置:"
puts JSON.pretty_generate(merged_config)📊 JSON性能优化
大型JSON处理
ruby
require 'json'
require 'stringio'
# 流式JSON解析(适用于大型文件)
class StreamJSONParser
def self.parse_large_file(file_path)
File.open(file_path, 'r') do |file|
# 对于非常大的JSON文件,可以考虑使用yajl-ruby等流式解析库
# 这里演示基本概念
json_string = file.read
JSON.parse(json_string)
end
end
# 分块处理JSON数组
def self.process_json_array_chunks(json_array, chunk_size = 1000)
json_array.each_slice(chunk_size) do |chunk|
yield chunk
end
end
end
# 创建大型JSON数据进行测试
large_data = {
users: (1..10000).map do |i|
{
id: i,
name: "用户#{i}",
email: "user#{i}@example.com",
score: rand(100)
}
end
}
# 写入文件
File.write('large_data.json', JSON.generate(large_data))
# 分块处理
# large_json = JSON.parse(File.read('large_data.json'))
# StreamJSONParser.process_json_array_chunks(large_json['users'], 1000) do |chunk|
# puts "处理块,大小: #{chunk.length}"
# # 处理每个块的数据
# endJSON缓存
ruby
require 'json'
require 'digest'
class JSONCache
def initialize(cache_dir = './json_cache')
@cache_dir = cache_dir
Dir.mkdir(@cache_dir) unless Dir.exist?(@cache_dir)
end
def fetch(key, expires_in = 3600)
cache_file = cache_file_path(key)
if File.exist?(cache_file)
cache_data = JSON.parse(File.read(cache_file))
if Time.now.to_i < cache_data['expires_at']
return cache_data['data']
else
File.delete(cache_file)
end
end
# 缓存未命中,执行块并缓存结果
data = yield
cache_data = {
data: data,
expires_at: Time.now.to_i + expires_in
}
File.write(cache_file, JSON.generate(cache_data))
data
end
private
def cache_file_path(key)
hash = Digest::MD5.hexdigest(key)
File.join(@cache_dir, "#{hash}.json")
end
end
# 使用JSON缓存
# cache = JSONCache.new
#
# # 缓存昂贵的计算结果
# result = cache.fetch('expensive_operation') do
# puts "执行昂贵操作..."
# sleep(2) # 模拟耗时操作
# { message: '操作完成', timestamp: Time.now.to_i }
# end
#
# puts JSON.pretty_generate(result)🔍 JSON调试工具
JSON格式化和验证
ruby
require 'json'
class JSONDebugger
# 格式化JSON字符串
def self.format_json(json_string, options = {})
begin
data = JSON.parse(json_string)
JSON.pretty_generate(data, options)
rescue JSON::ParserError => e
"无效的JSON: #{e.message}"
end
end
# 验证JSON字符串
def self.validate_json(json_string)
begin
JSON.parse(json_string)
{ valid: true, error: nil }
rescue JSON::ParserError => e
{ valid: false, error: e.message }
end
end
# 比较两个JSON对象
def self.compare_json(json1, json2)
data1 = json1.is_a?(String) ? JSON.parse(json1) : json1
data2 = json2.is_a?(String) ? JSON.parse(json2) : json2
if data1 == data2
{ equal: true, differences: [] }
else
differences = find_differences(data1, data2)
{ equal: false, differences: differences }
end
end
private
def self.find_differences(obj1, obj2, path = '')
differences = []
case [obj1, obj2].map(&:class)
when [Hash, Hash]
all_keys = (obj1.keys + obj2.keys).uniq
all_keys.each do |key|
current_path = path.empty? ? key.to_s : "#{path}.#{key}"
if !obj1.key?(key)
differences << { path: current_path, type: 'missing_in_first', value: obj2[key] }
elsif !obj2.key?(key)
differences << { path: current_path, type: 'missing_in_second', value: obj1[key] }
else
differences.concat(find_differences(obj1[key], obj2[key], current_path))
end
end
when [Array, Array]
max_length = [obj1.length, obj2.length].max
(0...max_length).each do |i|
current_path = "#{path}[#{i}]"
if i >= obj1.length
differences << { path: current_path, type: 'missing_in_first', value: obj2[i] }
elsif i >= obj2.length
differences << { path: current_path, type: 'missing_in_second', value: obj1[i] }
else
differences.concat(find_differences(obj1[i], obj2[i], current_path))
end
end
else
if obj1 != obj2
differences << { path: path, type: 'different_values', first: obj1, second: obj2 }
end
end
differences
end
end
# 使用调试工具
# 格式化JSON
messy_json = '{"name":"张三","age":25,"hobbies":["读书","游泳"],"address":{"city":"北京","district":"朝阳区"}}'
formatted = JSONDebugger.format_json(messy_json)
puts "格式化后的JSON:"
puts formatted
# 验证JSON
valid_json = '{"name": "张三", "age": 25}'
invalid_json = '{"name": "张三", "age": 25' # 缺少结束括号
puts "\n验证结果:"
puts "有效JSON: #{JSONDebugger.validate_json(valid_json)[:valid]}"
puts "无效JSON: #{JSONDebugger.validate_json(invalid_json)[:valid]}"
# 比较JSON
json1 = '{"name": "张三", "age": 25, "city": "北京"}'
json2 = '{"name": "张三", "age": 26, "city": "上海"}'
comparison = JSONDebugger.compare_json(json1, json2)
puts "\n比较结果:"
puts "是否相等: #{comparison[:equal]}"
puts "差异:"
comparison[:differences].each do |diff|
puts " 路径: #{diff[:path]}, 类型: #{diff[:type]}"
end🎯 JSON最佳实践
1. 错误处理
ruby
require 'json'
class SafeJSONHandler
# 安全解析JSON
def self.parse(json_string, options = {})
JSON.parse(json_string, options)
rescue JSON::ParserError => e
raise "JSON解析失败: #{e.message} (字符串: #{json_string[0, 100]}...)"
end
# 安全生成JSON
def self.generate(data, options = {})
JSON.generate(data, options)
rescue JSON::GeneratorError => e
raise "JSON生成失败: #{e.message}"
end
# 带默认值的解析
def self.parse_with_defaults(json_string, defaults = {})
parsed = parse(json_string, symbolize_names: true)
defaults.merge(parsed)
rescue
defaults
end
end
# 使用安全处理
begin
valid_json = '{"name": "张三", "age": 25}'
data = SafeJSONHandler.parse(valid_json, symbolize_names: true)
puts "解析成功: #{data}"
invalid_json = '{"name": "张三", "age": 25'
data = SafeJSONHandler.parse(invalid_json)
rescue => e
puts "捕获错误: #{e.message}"
end
# 使用默认值
defaults = { name: '未知', age: 0, city: '未知' }
json_with_missing_fields = '{"name": "李四", "age": 30}'
result = SafeJSONHandler.parse_with_defaults(json_with_missing_fields, defaults)
puts "合并结果: #{result}"2. 数据类型处理
ruby
require 'json'
# 处理特殊数据类型
class JSONTypeHandler
# 自定义序列化选项
def self.serialize_with_types(data)
JSON.generate(data, {
ascii_only: false,
max_nesting: 100
})
end
# 处理大数字
def self.handle_large_numbers
# JavaScript中数字精度限制
large_number = 9007199254740991 # Number.MAX_SAFE_INTEGER
data = { big_number: large_number }
json_string = JSON.generate(data)
parsed = JSON.parse(json_string)
puts "原始数字: #{large_number}"
puts "JSON字符串: #{json_string}"
puts "解析后数字: #{parsed['big_number']}"
puts "是否相等: #{large_number == parsed['big_number']}"
end
# 处理日期时间
def self.handle_datetime
now = Time.now
data = {
created_at: now,
updated_at: now.iso8601
}
# 默认序列化
json_default = JSON.generate(data)
puts "默认序列化: #{json_default}"
# 自定义序列化
json_custom = JSON.generate(data) do |obj|
case obj
when Time, Date, DateTime
obj.iso8601
else
obj
end
end
puts "自定义序列化: #{json_custom}"
end
end
# JSONTypeHandler.handle_large_numbers
# JSONTypeHandler.handle_datetime3. 安全处理
ruby
require 'json'
class SecureJSONHandler
# 防止深度嵌套攻击
def self.safe_parse(json_string, max_depth = 10)
JSON.parse(json_string, max_nesting: max_depth)
rescue JSON::NestingError
raise "JSON嵌套深度超过限制 (最大: #{max_depth})"
rescue JSON::ParserError => e
raise "JSON解析错误: #{e.message}"
end
# 限制JSON大小
def self.parse_with_size_limit(json_string, max_size = 1024 * 1024) # 1MB
if json_string.bytesize > max_size
raise "JSON大小超过限制 (最大: #{max_size} 字节)"
end
JSON.parse(json_string)
end
# 清理潜在危险内容
def self.sanitize_json(data)
case data
when Hash
data.each_with_object({}) do |(key, value), result|
# 移除潜在危险的键
clean_key = key.to_s.gsub(/[<>]/, '')
result[clean_key] = sanitize_json(value)
end
when Array
data.map { |item| sanitize_json(item) }
when String
# 移除潜在的脚本标签
data.gsub(/<script.*?>.*?<\/script>/mi, '')
else
data
end
end
end
# 使用安全处理
begin
# 正常JSON
normal_json = '{"name": "张三", "age": 25}'
data = SecureJSONHandler.safe_parse(normal_json)
puts "正常解析: #{data}"
# 深度嵌套JSON(模拟攻击)
# nested_json = '{"a":{"b":{"c":{"d":{"e":{"f":{"g":{"h":{"i":{"j":{"k":"too deep"}}}}}}}}}}}'
# data = SecureJSONHandler.safe_parse(nested_json)
rescue => e
puts "安全错误: #{e.message}"
end📚 下一步学习
掌握了Ruby JSON处理后,建议继续学习:
- Ruby 参考手册及学习资源 - 获取更多学习资料
- Ruby 数据库访问 - 学习数据库操作
- Ruby Web服务 - 深入Web开发
- Ruby 多线程 - 了解并发编程
继续您的Ruby学习之旅吧!