diff --git a/go.mod b/go.mod
index ed30cc032bdb6d967e55dcf82bef370a4a7e10d0..372a5f1f61d2f523d327d630e73fa21f0aad2eac 100644
--- a/go.mod
+++ b/go.mod
@@ -62,8 +62,8 @@ require (
 	go.opentelemetry.io/otel/metric v1.24.0
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/sync v0.6.0 // indirect
-	golang.org/x/sys v0.20.0 // indirect
-	golang.org/x/text v0.15.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
+	golang.org/x/text v0.15.0
 	google.golang.org/appengine v1.6.8 // indirect
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240521202816-d264139d666e // indirect
 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
diff --git a/go.sum b/go.sum
index 9a372e53de2bdc61ea06ca757784389c4cb1f383..7f00103fa5b26228e50be943d05c23325cbdc38c 100644
--- a/go.sum
+++ b/go.sum
@@ -151,8 +151,8 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
-golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/pkg/schema/schema.go b/pkg/schema/schema.go
index a99f7b53f8a48253ddf620006b10f1ed8cdca97b..1c3567e12756ac434de98a2dd7d25bb9b5f19ccb 100644
--- a/pkg/schema/schema.go
+++ b/pkg/schema/schema.go
@@ -2,6 +2,8 @@ package schema
 
 import (
 	"context"
+	"os"
+	"path/filepath"
 	"reflect"
 
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
@@ -25,6 +27,41 @@ func NewFromField(f *field.Field) *Schema {
 	return &Schema{Field: *f}
 }
 
+// FromFile инициализирует и возвращает объект схемы из файла
+// Поддерживаются форматы JSON и YAML
+func FromFile(path string) (*Schema, error) {
+	file, err := os.Open(path)
+	if err != nil {
+		return nil, err
+	}
+	defer file.Close()
+
+	switch filepath.Ext(path) {
+	case ".json":
+		return FromJSON(file)
+	case ".yaml", ".yml":
+		return FromYAML(file)
+	}
+
+	return nil, errors.New("schema must be in JSON or YAML format")
+}
+
+// FromFiles возвращает все валидные схемы в переданной директории
+func FromFiles(path string) ([]*Schema, error) {
+	files, err := os.ReadDir(path)
+	if err != nil {
+		return nil, err
+	}
+
+	result := make([]*Schema, 0, len(files))
+	for _, file := range files {
+		if schema, err := FromFile(filepath.Join(path, file.Name())); err == nil {
+			result = append(result, schema)
+		}
+	}
+	return result, nil
+}
+
 var (
 	Encode   = field.Encode
 	Decode   = field.Decode
diff --git a/pkg/schema/schema_json.go b/pkg/schema/schema_json.go
index e8710f76dfb8a5a81da279e4ba6d46f77a1bbdb8..2253923067a0dce2ab47a9e8055e6bc1562fb526 100644
--- a/pkg/schema/schema_json.go
+++ b/pkg/schema/schema_json.go
@@ -1,10 +1,23 @@
 package schema
 
 import (
+	"io"
+
 	"git.perx.ru/perxis/perxis-go/pkg/errors"
 	jsoniter "github.com/json-iterator/go"
 )
 
+func FromJSON(r io.Reader) (s *Schema, err error) {
+	data, err := io.ReadAll(r)
+	if err != nil {
+		return nil, err
+	}
+
+	s = New()
+	err = s.UnmarshalJSON(data)
+	return s, err
+}
+
 type jsonSchema struct {
 	Loaded   bool              `json:"loaded"`
 	Metadata map[string]string `json:"metadata"`
@@ -23,15 +36,6 @@ func (s *Schema) UnmarshalJSON(b []byte) error {
 		return err
 	}
 
-	//if len(j.Field) > 0 {
-	//	if err := s.Field.UnmarshalJSON(j.Field); err != nil {
-	//		return err
-	//	}
-	//	//if err := jsoniter.Unmarshal(j.Field, &s.Field); err != nil {
-	//	//	return err
-	//	//}
-	//}
-
 	return nil
 }
 
diff --git a/pkg/schema/schema_yaml.go b/pkg/schema/schema_yaml.go
new file mode 100644
index 0000000000000000000000000000000000000000..7bfe6365ca648649ce353e8534aead2d4f2f2419
--- /dev/null
+++ b/pkg/schema/schema_yaml.go
@@ -0,0 +1,55 @@
+package schema
+
+import (
+	"io"
+
+	jsoniter "github.com/json-iterator/go"
+	"gopkg.in/yaml.v3"
+)
+
+func FromYAML(r io.Reader) (s *Schema, err error) {
+	yml, err := io.ReadAll(r)
+	if err != nil {
+		return nil, err
+	}
+
+	s = New()
+	err = s.UnmarshalYAML(yml)
+	return s, err
+}
+
+func (s *Schema) UnmarshalYAML(b []byte) error {
+	jsonData, err := yamlToJson(b)
+	if err != nil {
+		return err
+	}
+
+	return s.UnmarshalJSON(jsonData)
+}
+
+func (s *Schema) MarshalYAML() ([]byte, error) {
+	jsonData, err := s.MarshalJSON()
+	if err != nil {
+		return nil, err
+	}
+
+	return jsonToYaml(jsonData)
+}
+
+func jsonToYaml(b []byte) ([]byte, error) {
+	var data interface{}
+	if err := jsoniter.Unmarshal(b, &data); err != nil {
+		return nil, err
+	}
+
+	return yaml.Marshal(data)
+}
+
+func yamlToJson(b []byte) ([]byte, error) {
+	var data interface{}
+	if err := yaml.Unmarshal(b, &data); err != nil {
+		return nil, err
+	}
+
+	return jsoniter.Marshal(data)
+}
diff --git a/pkg/schema/test/assets/invalid.json b/pkg/schema/test/assets/invalid.json
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pkg/schema/test/assets/not_schema.txt b/pkg/schema/test/assets/not_schema.txt
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/pkg/schema/test/assets/web_pages.json b/pkg/schema/test/assets/web_pages.json
new file mode 100644
index 0000000000000000000000000000000000000000..652753c911e79dca0896b2c57587400202d0da69
--- /dev/null
+++ b/pkg/schema/test/assets/web_pages.json
@@ -0,0 +1,420 @@
+{
+    "ui": {
+        "widget": "Tabs",
+        "options": {
+            "description": "path",
+            "collection_icon": "ApartmentOutlined/FileTextOutlined",
+            "fields": [
+                "content",
+                "seo",
+                "settings",
+                "advanced",
+                "design",
+                "variables"
+            ],
+            "title": "name",
+            "key": "path"
+        },
+        "list_view": {
+            "options": {
+                "sort": [
+                    "path"
+                ],
+                "page_size": 50,
+                "fields": [
+                    "path",
+                    "name",
+                    "updated_at",
+                    "updated_by",
+                    "state"
+                ]
+            }
+        }
+    },
+    "includes": [
+        {
+            "ref": "hoop_item_options",
+            "optional": true
+        },
+        {
+            "ref": "ext_web_pages_*",
+            "optional": true
+        }
+    ],
+    "type": "object",
+    "params": {
+        "inline": false,
+        "fields": {
+            "design": {
+                "title": "Дизайн",
+                "includes": [
+                    {
+                        "ref": "web_design"
+                    }
+                ],
+                "type": "object",
+                "params": {
+                    "inline": false,
+                    "fields": {}
+                }
+            },
+            "variables": {
+                "title": "Переменные",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "variables"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": true,
+                    "fields": {
+                        "variables": {
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "ui": {
+                                        "options": {
+                                            "collection_icon": "SettingOutlined/FileExcelOutlined",
+                                            "fields": [
+                                                "id",
+                                                "name",
+                                                "value"
+                                            ]
+                                        },
+                                        "list_view": {
+                                            "options": {
+                                                "fields": [
+                                                    "id",
+                                                    "name",
+                                                    "updated_at",
+                                                    "updated_by",
+                                                    "state"
+                                                ],
+                                                "page_size": 50
+                                            }
+                                        }
+                                    },
+                                    "type": "object",
+                                    "params": {
+                                        "inline": false,
+                                        "fields": {
+                                            "id": {
+                                                "title": "Идентификатор переменной",
+                                                "text_search": true,
+                                                "options": {
+                                                    "required": true
+                                                },
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "name": {
+                                                "title": "Название переменной",
+                                                "text_search": true,
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "value": {
+                                                "title": "Значение переменной",
+                                                "type": "string",
+                                                "params": {}
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+            "content": {
+                "title": "Содержимое страницы",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "blocks"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": true,
+                    "fields": {
+                        "blocks": {
+                            "title": "Блоки",
+                            "ui": {
+                                "widget": "BlockList",
+                                "options": {
+                                    "create": {
+                                        "classes": [
+                                            "class_web_blocks"
+                                        ]
+                                    }
+                                }
+                            },
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "title": "Блок",
+                                    "type": "reference",
+                                    "params": {
+                                        "allowedCollections": [
+                                            "web_block_*"
+                                        ]
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            },
+            "seo": {
+                "title": "SEO",
+                "includes": [
+                    {
+                        "ref": "web_seo"
+                    }
+                ],
+                "type": "object",
+                "params": {
+                    "inline": false,
+                    "fields": {}
+                }
+            },
+            "settings": {
+                "title": "Настройки",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "name",
+                            "parent",
+                            "path",
+                            "slug",
+                            "section",
+                            "datasource",
+                            "redirect",
+                            "redirect_path",
+                            "redirect_url",
+                            "outputs",
+                            "navTitle",
+                            "navHide",
+                            "weight"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": true,
+                    "fields": {
+                        "path": {
+                            "title": "Путь",
+                            "unique": true,
+                            "text_search": true,
+                            "options": {
+                                "value": "$perxis.Item.Template == true ? _value : parent == nil ?  '/' : slug == nil ? make_path(parent, slugify(replace_markers(name))) : make_path(parent, slugify(replace_markers(slug)))"
+                            },
+                            "type": "string",
+                            "params": {}
+                        },
+                        "redirect_url": {
+                            "title": "URL для перехода",
+                            "condition": "redirect == true",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "outputs": {
+                            "title": "Форматы страницы для вывода",
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "ui": {
+                                        "widget": "Lookup",
+                                        "options": {
+                                            "allowedCollections": [
+                                                {
+                                                    "collection": "web_outputs"
+                                                }
+                                            ]
+                                        }
+                                    },
+                                    "type": "string",
+                                    "params": {}
+                                }
+                            }
+                        },
+                        "weight": {
+                            "title": "Порядок следования",
+                            "ui": {
+                                "widget": "NumberInput"
+                            },
+                            "type": "number",
+                            "params": {
+                                "format": "int"
+                            }
+                        },
+                        "navHide": {
+                            "title": "Скрыть страницу из навигации",
+                            "ui": {
+                                "widget": "Checkbox"
+                            },
+                            "type": "bool",
+                            "params": {}
+                        },
+                        "section": {
+                            "title": "Раздел",
+                            "ui": {
+                                "widget": "Checkbox"
+                            },
+                            "type": "bool",
+                            "params": {}
+                        },
+                        "parent": {
+                            "title": "Родительский раздел",
+                            "description": "Раздел сайта, где расположена страница",
+                            "ui": {
+                                "widget": "Lookup",
+                                "options": {
+                                    "allowedCollections": [
+                                        {
+                                            "collection": "web_pages"
+                                        }
+                                    ]
+                                }
+                            },
+                            "type": "string",
+                            "params": {}
+                        },
+                        "name": {
+                            "title": "Название",
+                            "indexed": true,
+                            "text_search": true,
+                            "options": {
+                                "required": true,
+                                "value": "$perxis.Item.Template == true ? _value : replace_markers(_value)"
+                            },
+                            "type": "string",
+                            "params": {}
+                        },
+                        "navTitle": {
+                            "title": "Название для навигации",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "redirect_path": {
+                            "title": "Страница для перехода",
+                            "ui": {
+                                "widget": "Lookup",
+                                "options": {
+                                    "allowedCollections": [
+                                        {
+                                            "collection": "web_pages"
+                                        }
+                                    ]
+                                }
+                            },
+                            "condition": "redirect == true",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "datasource": {
+                            "title": "Источник данных",
+                            "description": "Источник данных из которого будут формироваться подстраницы раздела",
+                            "ui": {
+                                "widget": "Lookup",
+                                "options": {
+                                    "allowedCollections": [
+                                        {
+                                            "collection": "web_datasources"
+                                        }
+                                    ]
+                                }
+                            },
+                            "condition": "section == true",
+                            "type": "string",
+                            "params": {}
+                        },
+                        "redirect": {
+                            "title": "Перенаправление",
+                            "description": "Страница не имеет содержимого и перенаправляет посетителей на другой адрес",
+                            "ui": {
+                                "widget": "Checkbox"
+                            },
+                            "type": "bool",
+                            "params": {}
+                        },
+                        "slug": {
+                            "title": "Slug",
+                            "description": "Идентификатор страницы в адресе URL",
+                            "text_search": true,
+                            "options": {
+                                "value": "$perxis.Item.Template == true ? _value : parent == nil ? nil : _value == nil ?  slugify(replace_markers(name)) :  slugify(replace_markers(_value))"
+                            },
+                            "type": "string",
+                            "params": {}
+                        }
+                    }
+                }
+            },
+            "advanced": {
+                "title": "Расширенные настройки",
+                "ui": {
+                    "options": {
+                        "fields": [
+                            "scripts"
+                        ]
+                    }
+                },
+                "type": "object",
+                "params": {
+                    "inline": false,
+                    "fields": {
+                        "scripts": {
+                            "title": "Дополнительные скрипты",
+                            "type": "array",
+                            "params": {
+                                "item": {
+                                    "ui": {
+                                        "options": {
+                                            "fields": [
+                                                "src",
+                                                "type",
+                                                "content"
+                                            ]
+                                        }
+                                    },
+                                    "type": "object",
+                                    "params": {
+                                        "inline": false,
+                                        "fields": {
+                                            "type": {
+                                                "title": "Media Type скрипта",
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "content": {
+                                                "title": "Содержимое скрипта",
+                                                "type": "string",
+                                                "params": {}
+                                            },
+                                            "src": {
+                                                "title": "URL для загрузки скрипта",
+                                                "type": "string",
+                                                "params": {}
+                                            }
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    },
+    "loaded": false,
+    "metadata": {
+        "extension": "perxisweb"
+    }
+}
\ No newline at end of file
diff --git a/pkg/schema/test/assets/web_pages.yml b/pkg/schema/test/assets/web_pages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7fdfdd24fa9dcaa04d31ca1506955c86d6e3ce43
--- /dev/null
+++ b/pkg/schema/test/assets/web_pages.yml
@@ -0,0 +1,297 @@
+---
+ui:
+  widget: Tabs
+  options:
+    description: path
+    collection_icon: ApartmentOutlined/FileTextOutlined
+    fields:
+    - content
+    - seo
+    - settings
+    - advanced
+    - design
+    - variables
+    title: name
+    key: path
+  list_view:
+    options:
+      sort:
+      - path
+      page_size: 50
+      fields:
+      - path
+      - name
+      - updated_at
+      - updated_by
+      - state
+includes:
+- ref: hoop_item_options
+  optional: true
+- ref: ext_web_pages_*
+  optional: true
+type: object
+params:
+  inline: false
+  fields:
+    design:
+      title: Дизайн
+      includes:
+      - ref: web_design
+      type: object
+      params:
+        inline: false
+        fields: {}
+    variables:
+      title: Переменные
+      ui:
+        options:
+          fields:
+          - variables
+      type: object
+      params:
+        inline: true
+        fields:
+          variables:
+            type: array
+            params:
+              item:
+                ui:
+                  options:
+                    collection_icon: SettingOutlined/FileExcelOutlined
+                    fields:
+                    - id
+                    - name
+                    - value
+                  list_view:
+                    options:
+                      fields:
+                      - id
+                      - name
+                      - updated_at
+                      - updated_by
+                      - state
+                      page_size: 50
+                type: object
+                params:
+                  inline: false
+                  fields:
+                    id:
+                      title: Идентификатор переменной
+                      text_search: true
+                      options:
+                        required: true
+                      type: string
+                      params: {}
+                    name:
+                      title: Название переменной
+                      text_search: true
+                      type: string
+                      params: {}
+                    value:
+                      title: Значение переменной
+                      type: string
+                      params: {}
+    content:
+      title: Содержимое страницы
+      ui:
+        options:
+          fields:
+          - blocks
+      type: object
+      params:
+        inline: true
+        fields:
+          blocks:
+            title: Блоки
+            ui:
+              widget: BlockList
+              options:
+                create:
+                  classes:
+                  - class_web_blocks
+            type: array
+            params:
+              item:
+                title: Блок
+                type: reference
+                params:
+                  allowedCollections:
+                  - web_block_*
+    seo:
+      title: SEO
+      includes:
+      - ref: web_seo
+      type: object
+      params:
+        inline: false
+        fields: {}
+    settings:
+      title: Настройки
+      ui:
+        options:
+          fields:
+          - name
+          - parent
+          - path
+          - slug
+          - section
+          - datasource
+          - redirect
+          - redirect_path
+          - redirect_url
+          - outputs
+          - navTitle
+          - navHide
+          - weight
+      type: object
+      params:
+        inline: true
+        fields:
+          path:
+            title: Путь
+            unique: true
+            text_search: true
+            options:
+              value: "$perxis.Item.Template == true ? _value : parent == nil ?  '/'
+                : slug == nil ? make_path(parent, slugify(replace_markers(name)))
+                : make_path(parent, slugify(replace_markers(slug)))"
+            type: string
+            params: {}
+          redirect_url:
+            title: URL для перехода
+            condition: redirect == true
+            type: string
+            params: {}
+          outputs:
+            title: Форматы страницы для вывода
+            type: array
+            params:
+              item:
+                ui:
+                  widget: Lookup
+                  options:
+                    allowedCollections:
+                    - collection: web_outputs
+                type: string
+                params: {}
+          weight:
+            title: Порядок следования
+            ui:
+              widget: NumberInput
+            type: number
+            params:
+              format: int
+          navHide:
+            title: Скрыть страницу из навигации
+            ui:
+              widget: Checkbox
+            type: bool
+            params: {}
+          section:
+            title: Раздел
+            ui:
+              widget: Checkbox
+            type: bool
+            params: {}
+          parent:
+            title: Родительский раздел
+            description: Раздел сайта, где расположена страница
+            ui:
+              widget: Lookup
+              options:
+                allowedCollections:
+                - collection: web_pages
+            type: string
+            params: {}
+          name:
+            title: Название
+            indexed: true
+            text_search: true
+            options:
+              required: true
+              value: "$perxis.Item.Template == true ? _value : replace_markers(_value)"
+            type: string
+            params: {}
+          navTitle:
+            title: Название для навигации
+            type: string
+            params: {}
+          redirect_path:
+            title: Страница для перехода
+            ui:
+              widget: Lookup
+              options:
+                allowedCollections:
+                - collection: web_pages
+            condition: redirect == true
+            type: string
+            params: {}
+          datasource:
+            title: Источник данных
+            description: Источник данных из которого будут формироваться подстраницы
+              раздела
+            ui:
+              widget: Lookup
+              options:
+                allowedCollections:
+                - collection: web_datasources
+            condition: section == true
+            type: string
+            params: {}
+          redirect:
+            title: Перенаправление
+            description: Страница не имеет содержимого и перенаправляет посетителей
+              на другой адрес
+            ui:
+              widget: Checkbox
+            type: bool
+            params: {}
+          slug:
+            title: Slug
+            description: Идентификатор страницы в адресе URL
+            text_search: true
+            options:
+              value: "$perxis.Item.Template == true ? _value : parent == nil ? nil
+                : _value == nil ?  slugify(replace_markers(name)) :  slugify(replace_markers(_value))"
+            type: string
+            params: {}
+    advanced:
+      title: Расширенные настройки
+      ui:
+        options:
+          fields:
+          - scripts
+      type: object
+      params:
+        inline: false
+        fields:
+          scripts:
+            title: Дополнительные скрипты
+            type: array
+            params:
+              item:
+                ui:
+                  options:
+                    fields:
+                    - src
+                    - type
+                    - content
+                type: object
+                params:
+                  inline: false
+                  fields:
+                    type:
+                      title: Media Type скрипта
+                      type: string
+                      params: {}
+                    content:
+                      title: Содержимое скрипта
+                      type: string
+                      params: {}
+                    src:
+                      title: URL для загрузки скрипта
+                      type: string
+                      params: {}
+loaded: false
+metadata:
+  extension: perxisweb
diff --git a/pkg/schema/test/convert_test.go b/pkg/schema/test/convert_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..51ab33a6751440672c8a9a0dc0038f11898749b2
--- /dev/null
+++ b/pkg/schema/test/convert_test.go
@@ -0,0 +1,211 @@
+package test
+
+import (
+	"testing"
+
+	"git.perx.ru/perxis/perxis-go/pkg/extension"
+	"git.perx.ru/perxis/perxis-go/pkg/references"
+	"git.perx.ru/perxis/perxis-go/pkg/schema"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/field"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/modify"
+	"git.perx.ru/perxis/perxis-go/pkg/schema/validate"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+func TestFromFile(t *testing.T) {
+	for _, tt := range []struct {
+		path       string
+		wantSchema *schema.Schema
+		wantErr    string
+	}{
+		{
+			path:    "assets/not_schema.txt",
+			wantErr: "schema must be in JSON or YAML format",
+		},
+		{
+			path:    "non/existent.json",
+			wantErr: "open non/existent.json: no such file or directory",
+		},
+		{
+			path:    "assets/invalid.json",
+			wantErr: "error unmarshal json into field",
+		},
+		{
+			path:       "assets/web_pages.json",
+			wantSchema: getPagesSchema(),
+		},
+		{
+			path:       "assets/web_pages.yml",
+			wantSchema: getPagesSchema(),
+		},
+	} {
+		t.Run(tt.path, func(t *testing.T) {
+			result, err := schema.FromFile(tt.path)
+			if tt.wantErr != "" {
+				require.Error(t, err)
+				assert.ErrorContains(t, err, tt.wantErr)
+				return
+			}
+
+			require.NoError(t, err)
+			require.Equal(t, tt.wantSchema, result)
+		})
+	}
+}
+
+func TestFromFiles(t *testing.T) {
+	t.Run("Non-existen path", func(t *testing.T) {
+		schemas, err := schema.FromFiles("non-existen")
+		require.Error(t, err)
+		require.ErrorContains(t, err, "no such file or directory")
+		require.Nil(t, schemas)
+	})
+
+	t.Run("Success", func(t *testing.T) {
+		schemas, err := schema.FromFiles("assets")
+		require.NoError(t, err)
+		require.Len(t, schemas, 2, "В директории хранятся две корректные схемы")
+		require.Equal(t, schemas[0], schemas[1], "Они одинаковые, но в разных форматах")
+	})
+}
+
+// Оригинальное объявление схемы Web/Страницы
+// Значение констант подставлено вручную
+func getPagesSchema() *schema.Schema {
+	content := field.Object(
+		true, "blocks", field.Array(
+			references.Field([]string{"web_block_*"}).SetTitle("Блок"),
+		).
+			SetTitle("Блоки").
+			WithUI(&field.UI{
+				Widget: "BlockList",
+				Options: map[string]interface{}{
+					"create": map[string]interface{}{
+						"classes": []string{"class_web_blocks"},
+					},
+				},
+			}),
+	).SetTitle("Содержимое страницы")
+
+	// SEO
+	seo := field.Object().WithIncludes("web_seo").SetTitle("SEO")
+
+	//Settings
+	settings := field.Object(true,
+		"name", field.String(
+			validate.Required(),
+			modify.Value("$perxis.Item.Template == true ? _value : replace_markers(_value)"),
+		).SetTitle("Название").SetTextSearch(true).SetIndexed(true),
+		"parent", field.String().SetTitle("Родительский раздел").SetDescription("Раздел сайта, где расположена страница").WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_pages"},
+				},
+			},
+		}), // TODO lookup "section == true"
+		"path", field.String(
+			modify.Value("$perxis.Item.Template == true ? _value : parent == nil ?  '/' : slug == nil ? make_path(parent, slugify(replace_markers(name))) : make_path(parent, slugify(replace_markers(slug)))"),
+		).SetTitle("Путь").SetUnique(true).SetTextSearch(true), // TODO readonly
+		"slug", field.String(modify.Value("$perxis.Item.Template == true ? _value : parent == nil ? nil : _value == nil ?  slugify(replace_markers(name)) :  slugify(replace_markers(_value))")).SetTitle("Slug").SetDescription("Идентификатор страницы в адресе URL").SetTextSearch(true),
+		"section", field.Bool().SetTitle("Раздел").WithUI(&field.UI{Widget: "Checkbox"}),
+		"datasource", field.String().WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_datasources"},
+				},
+			},
+		}).SetTitle("Источник данных").SetDescription("Источник данных из которого будут формироваться подстраницы раздела").SetCondition("section == true"),
+		"redirect", field.Bool().SetTitle("Перенаправление").SetDescription("Страница не имеет содержимого и перенаправляет посетителей на другой адрес").WithUI(&field.UI{Widget: "Checkbox"}),
+		"redirect_path", field.String().SetTitle("Страница для перехода").WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_pages"},
+				},
+			},
+		}).SetCondition("redirect == true"),
+		"redirect_url", field.String().SetTitle("URL для перехода").SetCondition("redirect == true"),
+		"outputs", field.Array(field.String().WithUI(&field.UI{
+			Widget: "Lookup",
+			Options: map[string]interface{}{
+				"allowedCollections": []interface{}{
+					map[string]interface{}{"collection": "web_outputs"},
+				},
+			},
+		})).SetTitle("Форматы страницы для вывода"),
+		"navTitle", field.String().SetTitle("Название для навигации"),
+		"navHide", field.Bool().SetTitle("Скрыть страницу из навигации").WithUI(&field.UI{Widget: "Checkbox"}),
+		"weight", field.Number(field.NumberFormatInt).SetTitle("Порядок следования").WithUI(&field.UI{Widget: "NumberInput"}),
+	).SetTitle("Настройки")
+
+	// Advanced
+	advanced := field.Object(
+		"scripts", field.Array(
+			field.Object(
+				"src", field.String().SetTitle("URL для загрузки скрипта"),
+				"type", field.String().SetTitle("Media Type скрипта"),
+				"content", field.String().SetTitle("Содержимое скрипта"),
+			),
+		).SetTitle("Дополнительные скрипты"),
+	).SetTitle("Расширенные настройки")
+
+	// Design
+	design := field.Object().WithIncludes("web_design").SetTitle("Дизайн")
+
+	//Variables
+	variables := field.Object(true, "variables", field.Array(getVarsField())).SetTitle("Переменные")
+
+	// Page
+	page := schema.New(
+		"content", content,
+		"seo", seo,
+		"settings", settings,
+		"advanced", advanced,
+		"design", design,
+		"variables", variables,
+	).WithMetadata(extension.MetadataKey, "perxisweb")
+
+	// Includes
+	page.SetIncludes(
+		field.Include{Ref: "hoop_item_options", Optional: true},
+		field.Include{Ref: "ext_web_pages_*", Optional: true},
+	)
+
+	//UI
+	page.Field.UI.ListView = &field.View{Options: map[string]interface{}{
+		"fields":    []string{"path", "name", "updated_at", "updated_by", "state"},
+		"sort":      []string{"path"},
+		"page_size": 50,
+	}}
+	page.Field.UI.Options["title"] = "name"
+	page.Field.UI.Options["key"] = "path"
+	page.Field.UI.Options["description"] = "path"
+	page.Field.UI.Widget = "Tabs"
+	page.Field.UI.Options["collection_icon"] = "ApartmentOutlined/FileTextOutlined"
+
+	_ = page.ConvertTypes()
+	return page
+}
+
+func getVarsSchema() (sch *schema.Schema) {
+	sch = schema.New(
+		"id", field.String(validate.Required()).SetTextSearch(true).SetTitle("Идентификатор переменной"),
+		"name", field.String().SetTextSearch(true).SetTitle("Название переменной"),
+		"value", field.String().SetTitle("Значение переменной"),
+	).WithMetadata(extension.MetadataKey, "perxisweb")
+	//UI
+	sch.Field.UI.ListView = &field.View{Options: map[string]interface{}{
+		"fields":    []string{"id", "name", "updated_at", "updated_by", "state"},
+		"page_size": 50,
+	}}
+
+	sch.Field.UI.Options["collection_icon"] = "SettingOutlined/FileExcelOutlined"
+	return sch
+}
+
+func getVarsField() *field.Field {
+	return &getVarsSchema().Field
+}
diff --git a/pkg/schema/test/object_test.go b/pkg/schema/test/object_test.go
index f17b64511668ad8b4a86c5d91cb908a781384493..6b53b0ad1993305bce4c6b3797b8c5fe5cffc01c 100644
--- a/pkg/schema/test/object_test.go
+++ b/pkg/schema/test/object_test.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
+	"strings"
 	"testing"
 	"time"
 
@@ -292,6 +293,82 @@ func TestSchemaUI_UnmarshalJSON(t *testing.T) {
 	assert.Equal(t, sch, schm)
 }
 
+func TestSchemaUI_UnmarshalYAML(t *testing.T) {
+	vw := &field.View{
+		Widget:  "Widget",
+		Options: map[string]interface{}{"title": "name", "key": "name"},
+	}
+	ui := &field.UI{
+		Widget:      "Widget",
+		Placeholder: "Placeholder",
+		Options:     map[string]interface{}{"title": "name", "key": "name"},
+		ListView:    vw,
+		ReadView:    vw,
+		EditView:    vw,
+	}
+	schm := schema.New(
+		"name", field.String().WithUI(ui),
+	)
+	schm.UI = ui
+
+	j := `
+---
+ui:
+  widget: Widget
+  placeholder: Placeholder
+  options:
+    title: name
+    key: name
+  read_view:
+    widget: Widget
+    options:
+      title: name
+      key: name
+  edit_view:
+    widget: Widget
+    options:
+      title: name
+      key: name
+  list_view:
+    widget: Widget
+    options:
+      title: name
+      key: name
+type: object
+params:
+  inline: false
+  fields:
+    name:
+      ui:
+        widget: Widget
+        placeholder: Placeholder
+        options:
+          title: name
+          key: name
+        read_view:
+          widget: Widget
+          options:
+            title: name
+            key: name
+        edit_view:
+          widget: Widget
+          options:
+            title: name
+            key: name
+        list_view:
+          widget: Widget
+          options:
+            title: name
+            key: name
+      type: string
+      params: {}
+loaded: false
+`
+	sch, err := schema.FromYAML(strings.NewReader(j))
+	require.NoError(t, err)
+	assert.Equal(t, sch, schm)
+}
+
 func TestSchema_GetField(t *testing.T) {
 
 	sch := schema.New(