---
description: "将模型与关联转换为数组或 JSON，并通过 hidden、visible 等属性控制序列化时包含哪些字段，便于构建干净、稳定的 API 响应。"
---

# 序列化

构建 JSON API 时，经常需要把模型和关联转化为对象或 JSON。针对这些操作，Sutando 提供了一些便捷方法，以及对序列化中的属性控制。 

## 序列化模型 & 集合 {#序列化模型-集合}

### 序列化为对象

要转化模型及其加载的关联为对象，可以使用 `toData` 方法。这是一个递归的方法，因此所有的属性和关联（包括关联的关联）都将转化成数组：

```ts
const user = await User.query().with('roles').first();

return user.toData();
```

`attributesToData` 方法可用于将模型的属性转换为对象，但不会转换其关联：

```ts
const user = await User.query().first();
 
return user.attributesToData();
```

你还可以通过调用集合实例上的 `toData` 方法，将模型的全部集合转换为对象： 

```ts
const users = await User.query().all();
 
return users.toData();
```

### 序列化为 JSON

方法 `toJson` 可以把模型转化成 JSON。和方法 `toData` 一样， `toJson` 方法也是递归的，因此所有属性和关联都会转化成 JSON, 你还可以指定由 Javascript 支持的 JSON 选项： 

```ts
const user = await User.query().find(1);
 
return user.toJson();
 
return user.toJson(null, 2);
```

或者，你也可以将模型或集合转换为字符串，模型或集合上的 `toJson` 方法会自动调用：

```ts
const user = await User.query().find(1);

return String(user);

return JSON.stringify(user);
```

由于模型和集合在转化为字符串的时候会转成 JSON， 因此可以把 Sutando 对象直接从路由返回。Sutando 会把模型和集合序列化为 JSON，`c.json()` 原样输出即可：

```ts
import { Hono } from 'hono';

const app = new Hono();

app.get('/', async (c) => {
  const user = await User.query().find(1);

  return c.json(user);
});
```

#### 关联关系

当一个模型被转化为 JSON 的时候，它加载的关联关系也将自动转化为 JSON 对象被包含进来。同时，通过「小驼峰」定义的关联方法，关联的 JSON 属性将会是「蛇形」命名。

## 隐藏 JSON 属性

有时要将模型对象或 JSON 中的某些属性进行隐藏，比如密码。则可以在模型中添加 `hidden` 属性。模型序列化后，`hidden` 数组中列出的属性将不会被显示：

```ts
import { Model } from 'sutando';

class User extends Model {
  hidden = ['password'];
}
```

此外，也可以使用属性 `visible` 定义一个模型数组和 JSON 可见的「白名单」。转化后的数组或 JSON 不会出现其他的属性：

```ts
import { Model } from 'sutando';

class User extends Model {
  visible = ['first_name', 'last_name'];
}
```

### 临时修改可见属性

如果你想要在一个模型实例中显示隐藏的属性，你可以使用 `makeVisible` 方法。`makeVisible` 方法返回模型实例： 

```ts
user.makeVisible('attribute').toData();

user.makeVisible(['attribute', 'another_attribute']).toData();
```

相应地，如果你想要在一个模型实例中隐藏可见的属性，你可以使用 `makeHidden` 方法。

```ts
user.makeHidden('attribute').toData();

user.makeHidden(['attribute', 'another_attribute']).toData();
```

如果你想临时覆盖所有可见或隐藏的属性，你可以分别使用 `setVisible` 和 `setHidden` 方法:

```ts
user.setVisible(['id', 'name']).toData();

user.setHidden(['email', 'password', 'remember_token']).toData();
```

## 追加 JSON 值

有时，需要在模型转换为对象或 JSON 时添加一些数据库中不存在字段的对应属性。要实现这个功能，首先要定义一个访问器：

```ts
import { Model, Attribute } from 'sutando';

class User extends Model {
  attributeIsAdmin() {
    return Attribute.make({
      get: (value, attributes) => (attributes.admin === 'yes')
    });
  }
}
```

然后，在模型属性 `appends` 中添加该属性名。注意，尽管访问器使用「驼峰命名法」方式定义，但是属性名通常以「蛇形命名法」的方式来引用：

```ts
import { Model } from 'sutando';

class User extends Model {
  appends = ['is_admin'];
}
```

使用 `appends` 方法追加属性后，它将包含在模型的对象和 JSON 中。`appends` 数组中的属性也将遵循模型上配置的 `visible` 和 `hidden` 设置。

### 运行时追加

你可以在单个模型实例上使用 `append` 方法来追加属性。或者，使用 `setAppends` 方法来重写整个追加属性的数组： 

```ts
user.append('is_admin').toData();
 
user.setAppends(['is_admin']).toData();
```