npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

entity-framework

v7.0.0

Published

实体数据框架

Downloads

59

Readme

Entity Framework

CI

Enables experimental support for decorators, which is in stage 2 of the TC39 standardization process.

see more about experimentalDecorators

基本用法

一个类声明在继承 DataModel 后可视为一个模型声明, 该类声明的实例可以进行序列化反序列化操作.

class ResponseData<T> extends DataModel {
  @Mapping()
  public data?: T

  @Mapping()
  public msg?: string

  @Mapping()
  public code?: number

  public others?: unknown
}

Serialize

const res = new ResponseData<boolean>()
res.code = 0
res.data = true
res.msg = 'success'
res.others = 'anything'

const data: model.Data = res.serialize()

expect(data.others).toBeUndefined()
expect(data).toEqual({ data: true, msg: 'success', code: 0 })

Deserialize

const res = new ResponseData<boolean>()
const data: model.Data = { data: true, msg: 'success', code: 0 }

res.deserialize(data)

expect(res.code).toEqual(data.code)
expect(res.msg).toEqual(data.msg)
expect(res.data).toEqual(data.data)
expect(res.others).toBeUndefined()

更多示例请参考 测试用例

  • test/serialize.spec.ts
  • test/deserialize.spec.ts

概念介绍

  • 映射数据 Data

    原生 Object 对象数据, 可通过反序列化操作填充到模型实例中.

  • 模型 DataModel

    继承自 DataModel 的类型声明, 拥有 serializedeserialize 实例方法.

  • 注解 Annotation

    用于描述模型与映射数据的关联关系.

  • 元数据 Metadata

    用于描述模型的数据.

  • 操作命令 OperationCommand

    命令分为序列化命令与反序列化命令, 在执行序列化操作时, 所有的序列化命令按优先级(priority)升序执行; 执行反序列化操作时, 所有的反序列化命令按优先级(priority)升序执行.

  • 数据访问路径 DataAccessorPath

    描述数据在映射数据结构中所在位置的拥有特定语法的的字符串.

    {
      "id": 1,
      "name": "Xin Hua book store",
      "filters": [
        {
          "name": "5天精通数据格式转换"
        },
        "JavaScript 犀牛书",
        "编译原理 鲸书",
        "OOP 设计模式",
        "反射",
        "装饰器",
        "Jest TDD 测试驱动"
      ],
      "stores": [
        {
          "id": 2,
          "name": "Xin Hua book store 2"
        },
        {
          "id": 3,
          "name": "Xin Hua book store 3"
        },
        {
          "id": 4,
          "name": "Xin Hua book store 4"
        }
      ]
    }

    | 数据访问路径 | 映射数据 | 说明 | | -- | -- | -- | | @Mapping({ path: 'id' }) | 1 | 如果类型成员字段与path一致, 可缺省 | | @Mapping({ path: 'filters[0]' }) | { "name": "5天精通数据格式转换" } | 通过索引范文映射数据数组的特定位置 | | @Mapping({ path: 'filters[1:]' }) | ["JavaScript...", "编译...", ...] | filters数组的后6项数据 | | @Mapping({ path: 'stores' }) | [{"id": 2, "name": "..."}, ...] | stores数组的全部数据 |

更多用法

  • 递归数据类型映射

    class Pattern extends DataModel {
      @Mapping()
      id?: number
    
      @Mapping()
      name?: string
    
      @Mapping({ relatedEntityDescriptor: 'Pattern' })
      pattern?: Pattern
    }
    
    const pattern = new Pattern()
    pattern.id = 1
    pattern.name = 'P1'
    pattern.pattern = new Pattern()
    pattern.pattern.id = 2
    pattern.pattern.name = 'P2'
    
    const data: model.Data = pattern.serialize()
    
    expect(data.id).toBe(1)
    expect(data.name).toBe('P1')
    expect(data.pattern.id).toBe(2)
    expect(data.pattern.name).toBe('P2')
  • 将模型字段映射到多维数组

    class LogSource extends DataModel {
      @Mapping()
      name?: string
    }
    
    class Category extends DataModel {
      @Mapping()
      name?: string
    }
    
    class Rule extends DataModel {
      @Mapping({ relatedEntityDescriptor: 'LogSource', path: 'filters[0][1]' })
      logSource?: LogSource
    
      @Mapping({ relatedEntityDescriptor: 'Category', path: 'filters[1][2]' })
      category?: Category
    }
    
    const rule = new Rule()
    
    rule.logSource = new LogSource()
    rule.logSource.name = 'logSource'
    
    rule.category = new Category()
    rule.category.name = 'category'
    
    const data: model.Data = rule.serialize()
    
    expect(data.filters?.[0][1].name).toBe('logSource')
    expect(data.filters?.[1][2].name).toBe('category')
  • 将模型字段映射到对象中的数组切片

    class Foo extends DataModel {
      @Mapping({ path: 'filters[0]' })
      id?: number
    
      @Mapping({ path: 'filters[1]' })
      name?: string
    
      @Mapping({ path: 'filters[2:3]' })
      children?: string[]
    }
    
    const foo = new Foo()
    foo.id = 1
    foo.name = 'foo'
    foo.children = ['Hello', 'World']
    
    const data: model.Data = foo.serialize()
    
    expect(data.filters).toHaveLength(3)
    expect(data.filters[0]).toBe(1)
    expect(data.filters[1]).toBe('foo')
    expect(data.filters[2]).toBe('Hello')
    expect(data.filters[3]).toBeUndefined()
  • 将模型字段直接映射到数组切片

    class CategorySet extends DataModel {
      @Mapping({ path: '[0]' })
      id?: number
    
      @Mapping({ path: '[1:]' })
      categories?: string[]
    }
    
    const set = new CategorySet()
    set.id = 1
    set.categories = ['c1', 'c2', 'c3']
    
    const data: model.Data = set.serialize([]) // !!important!! 注意, 这里需要提供初始化数据
    
    expect(data[0]).toBe(1)
    expect(data[1]).toBe('c1')
    expect(data[2]).toBe('c2')
    expect(data[3]).toBe('c3')
    expect(data).toBeInstanceOf(Array)

注解

数据映射关系注解

用于自动序列化过程与反序列化过程中关联模型字段与映射数据的键

  • @Mapping(options?: MappingOptions)

    用来描述模型字段与映射数据关系的注解

    type MappingOptions = {
      /**
       * 映射路径(JSONPath)
      */
      path: string
      /**
       * 关联模型的名称 
      * 例如: Metric 表示指标, 或者 Metric[] 表示指标数组, 如果缺省则认为是一个 Object 或 Object[]
      */
      relatedEntityDescriptor?: string
    }

断言注解

在序列化过程与反序列化过程后对模型中的数据进行断言

  • @NotBeNull(msg?: string)

    SerializeDeserialize 时断言实例字段不会为 null, 断言失败时将 msg 信息以 throw new Error(msg) 的形式抛出.

  • @NotBeUndefined(msg?: string)

    SerializeDeserialize 时断言实例字段不会为 undefined, 断言失败时将 msg 信息以 throw new Error(msg) 的形式抛出.

  • @NotBeEmpty(msg?: string)

    SerializeDeserialize 时断言实例字段不会为 nullundefined, 断言失败时将 msg 信息以 throw new Error(msg) 的形式抛出. 等价于同时使用 @NotBeNull() @NotBeUndefined()

自定义序列化与反序列化

使用 @Mapping() 注解的自动序列化与反序列化无法覆盖某些特殊场景, 此时需要在DataModel派生类构造函数中编写自定义序列化函数(doSerialize)与反序列化函数(doDeSerialize). 一旦使用了自定义函数, 则忽略所有注解.

  • 自定义序列化

    class Company extends DataModel {
      constructor() {
        super()
    
        this.doSerialize = () => {
          return { id: (this.id ?? 0) + 244 }
        }
      }
    
      id?: number | null
    }
    
    const company = new Company()
    company.id = 11
    
    const data: model.Data = company.serialize()
    
    expect(data.id).toBe(255)
  • 自定义反序列化

    class Company extends DataModel {
      constructor() {
        super()
    
        this.doDeSerialize = (data: model.Data) => {
          this.id = data.aid
        }
      }
    
      id?: number | null
    }
    
    const data: model.Data = { aid: 255 }
    
    const company = new Company()
    company.deserialize(data)
    
    expect(company.id).toBe(255)