0 关注者 · 17 帖子

JSON(JavaScript 对象表示法)是一种轻量级的数据交换格式。适合人类轻松读写。

文章 Kelly Huang · 十月 23, 2025 15m read

img

在本节中,我们将探讨如何在 IRIS 中使用 Python 作为主要编程语言,在使用 Python 编写应用程序逻辑的同时仍能利用 IRIS 的强大功能。

使用方法 (irispython)

我们先来介绍官方操作方式,即使用 irispython 解释器。

您可以使用 irispython 解释器直接在 IRIS 中运行 Python 代码。 这样,您可以编写 Python 代码,并在 IRIS 应用程序的运行环境中执行相应代码。

什么是 irispython?

irispython 是位于 IRIS 安装目录 (<installation_directory>/bin/irispython) 下的 Python 解释器,用于在 IRIS 的运行环境中执行 Python 代码。

它的功能包括:

  • 设置 sys.path,以包含 IRIS Python 库和模块。
    • 此操作通过 <installation_directory>/lib/python/iris_site.py 中的 site.py 文件执行。
    • 如需了解详情,请参阅模块文章 Python 模块简介
  • 允许您导入 iris 模块,这是一个特殊模块,用于访问 IRIS 功能,例如实现任何 ObjectScript 类与 Python 的双向桥接。
  • 修复权限问题并动态加载 iris 内核库。

irispython 使用示例

您可以通过命令行运行 irispython 解释器:

<installation_directory>/bin/irispython

我们来运行一个简单的示例:

# src/python/article/irispython_example.py
import requests
import iris

def run():
    response = requests.get("https://2eb86668f7ab407989787c97ec6b24ba.api.mockbin.io/")

    my_dict = response.json()

    for key, value in my_dict.items():
        print(f"{key}: {value}")  # print message: Hello World

    return my_dict

if __name__ == "__main__":
    print(f"Iris version: {iris.cls('%SYSTEM.Version').GetVersion()}")
    run()

您可以使用 irispython解释器运行此脚本:

<installation_directory>/bin/irispython src/python/article/irispython_example.py

您将看到如下输出:

Iris version: IRIS for UNIX (Ubuntu Server LTS for x86-64 Containers) 2025.1 (Build 223U) Tue Mar 11 2025 18:23:31 EDT
message: Hello World

此例展示了如何使用 irispython 解释器在 IRIS 的运行环境中执行 Python 代码。

优点

  • Python 优先:您可以使用 Python 编写应用程序逻辑,这样,您可以利用 Python 的功能和库。
  • IRIS 集成:您可以轻松将 Python 代码与 IRIS 功能相集成。

缺点

  • 调试受限:在 irispython 中调试 Python 代码并不像在专用 Python 环境中那样简单直接。
    • 这并不是说无法进行调试,而是并不像在专用 Python 环境中那样简单。
    • 如需了解详情,请参阅补充部分
  • 虚拟环境:在 irispython 中为 Python 代码搭建虚拟环境比较困难。
    • 这并不是说无法搭建,只是操作起来比较困难。因为默认情况下,虚拟环境会查找名为 pythonpython3 的解释器,而 IRIS 中的情况并非如此。
    • 如需了解详情,请参阅补充部分

结论

总的来说,使用 irispython 解释器让您既可以利用 Python 编写应用程序逻辑,又能利用 IRIS 的强大功能。 不过,这种方式也存在调试和虚拟环境搭建方面的限制。

使用 WSGI

在本节中,我们将探讨如何使用 WSGI(Web 服务器网关接口)在 IRIS 中运行 Python Web 应用程序。

WSGI 是 Web 服务器与 Python Web 应用程序或框架之间的标准接口。 利用 WSGI,您可以在 Web 服务器环境中运行 Python Web 应用程序。

IRIS 支持 WSGI,这意味着您可以在 IRIS 中使用内置的 WSGI 服务器运行 Python Web 应用程序。

使用方法

要在 IRIS 中使用 WSGI,您需要创建 WSGI 应用程序,并向 IRIS Web 服务器注册此应用程序。

如需了解详情,请参阅官方文档

WSGI 使用示例

有关完整模板,请参见此处:iris-flask-example

优点

  • Python Web 框架:您可以使用流行的 Python Web 框架(如 Flask 或 Django)来构建 Web 应用程序。
  • IRIS 集成:您可以轻松将 Python Web 应用程序与 IRIS 功能相集成。

缺点

  • 复杂程度:构建 WSGI 应用程序会比直接在 Python Web 框架中使用 uvicorngunicorn 复杂一些。

结论

总的来说,在 IRIS 中使用 WSGI 让您既可以利用 Python 构建功能强大的 Web 应用程序,又能利用 IRIS 的功能。

DB-API

在本节中,我们将探讨如何使用 Python DB-API 与 IRIS 数据库进行交互。

Python DB-API 是 Python 中用于连接数据库的标准接口。 利用此接口,您可以执行 SQL 查询,并从数据库中检索结果。

使用方法

您可以使用 pip 进行安装:

pip install intersystems-irispython

随后,您可以使用 DB-API 连接 IRIS 数据库并执行 SQL 查询。

DB-API 使用示例

它的使用方法与其他所有 Python DB-API 相同,示例如下:

# src/python/article/dbapi_example.py
import iris

def run():
    # Connect to the IRIS database
# Open a connection to the server
    args = {
        'hostname':'127.0.0.1', 
        'port': 1972,
        'namespace':'USER', 
        'username':'SuperUser', 
        'password':'SYS'
    }
    conn = iris.connect(**args)

    # Create a cursor
    cursor = conn.cursor()

    # Execute a query
    cursor.execute("SELECT 1")

    # Fetch all results
    results = cursor.fetchall()

    for row in results:
        print(row)

    # Close the cursor and connection
    cursor.close()
    conn.close()
if __name__ == "__main__":
    run()

您可以使用任何 Python 解释器运行此脚本:

python3 /irisdev/app/src/python/article/dbapi_example.py

您将看到如下输出:

(1,)

优点

  • 标准接口:DB-API 提供用于连接数据库的标准接口,因此可以轻松切换不同的数据库。
  • SQL 查询:您可以使用 Python 执行 SQL 查询,并从数据库检索结果。
  • 远程访问:您可以使用 DB-API 连接到远程 IRIS 数据库。

缺点

  • 功能有限:DB-API 仅可通过 SQL 访问数据库,因此,您无法使用高级 IRIS 数据库功能,如执行 ObjectScript 或 Python 代码。

备选方案

还提供社区版 DB-API,参见此处:intersystems-irispython-community

该版本能更好地支持 SQLAlchemy、Django、langchain,以及其他使用 DB-API 的 Python 库。

如需了解详情,请参阅 补充部分

结论

总的来说,将 Python DB-API 与 IRIS 结合使用能够让您构建功能强大的应用程序,实现与数据库的无缝交互。

Notebook

现在,我们已了解如何在 IRIS 中使用 Python,接下来我们将探讨如何将 Jupyter Notebooks 与 IRIS 结合使用。

Jupyter Notebooks 是交互式编写和执行 Python 代码的绝佳方式,并且可与 IRIS 结合使用,以充分利用 IRIS 的功能。

使用方法

要在 IRIS 中使用 Jupyter Notebooks,您需要安装 notebookipykernel 这两个软件包:

pip install notebook ipykernel

然后,您可以创建新的 Jupyter Notebook 并选择 Python 3 内核。

Notebook 使用示例

您可以创建新的 Jupyter Notebook 并编写以下代码:

# src/python/article/my_notebook.ipynb
# Import the necessary modules
import iris
# Do the magic
iris.system.Version.GetVersion()

您可以使用 Jupyter Notebook 运行此 notebook:

jupyter notebook src/python/article/my_notebook.ipynb

优点

  • 交互式开发:利用 Jupyter Notebooks,您可以交互式编写和执行 Python 代码,非常适合数据分析和探索。
  • 丰富的输出:您可以直接在 Notebook 中显示丰富的输出,如图表和表格。
  • 文档:您可以在代码旁添加文档和说明。

缺点

  • 设置有难度:设置将 Jupyter Notebooks 与 IRIS 结合使用存在一定的难度,特别是对于内核配置而言。

结论

总的来说,将 Jupyter Notebooks 与 IRIS 结合使用可以交互式编写和执行 Python 代码,同时利用 IRIS 的功能。 不过,设置起来存在一定的难度,特别是对于内核配置而言。

补充部分

从本节开始,我们将探讨一些与在 IRIS 中使用 Python 相关的高级主题,例如远程调试 Python 代码、使用虚拟环境等。

以下大部分主题均未获得 InterSystems 的官方支持,但如果您要在 IRIS 中使用 Python,了解相关内容会提供很大的帮助。

使用原生解释器(无 irispython

在本节中,我们将探讨如何使用原生 Python 解释器代替 irispython 解释器。

这样一来,您可以直接使用虚拟环境,并使用您习惯的 Python 解释器。

使用方法

要使用原生 Python 解释器,您需要在机器本地安装 IRIS,并需要安装 iris-embedded-python-wrapper 软件包。

您可以使用 pip 进行安装:

pip install iris-embedded-python-wrapper

接下来,您需要设置一些环境变量指向 IRIS 安装目录:

export IRISINSTALLDIR=<installation_directory>
export IRISUSERNAME=<username>
export IRISPASSWORD=<password>
export IRISNAMESPACE=<namespace>

然后,您可以使用您的原生 Python 解释器运行 Python 代码:

python3 src/python/article/irispython_example.py
# src/python/article/irispython_example.py
import requests
import iris

def run():
    response = requests.get("https://2eb86668f7ab407989787c97ec6b24ba.api.mockbin.io/")

    my_dict = response.json()

    for key, value in my_dict.items():
        print(f"{key}: {value}")  # print message: Hello World

    return my_dict

if __name__ == "__main__":
    print(f"Iris version: {iris.cls('%SYSTEM.Version').GetVersion()}")
    run()

如需了解详情,请参阅 iris-embedded-python-wrapper 文档

优点

  • 虚拟环境:您可以将虚拟环境与原生 Python 解释器结合使用,从而可以更加轻松地管理依赖项。
  • 熟悉的工作流:您可以使用习惯的 Python 解释器,从而可以更轻松地与现有工作流相集成。
  • 调试:可以使用您喜欢的 Python 调试工具(如 pdbipdb)在 IRIS 中调试 Python 代码。

缺点

  • 设置的复杂程度:设置环境变量和 iris-embedded-python-wrapper 软件包可能会比较复杂,特别是对于初学者来说。
  • 未获官方支持:此方式未获 InterSystems 的官方支持,因此您可能遇到文档中未记录或不受支持的问题。

DB-API 社区版

在本节中,我们将探讨 GitHub 上提供的社区版 DB-API。

使用方法

您可以使用 pip 进行安装:

pip install sqlalchemy-iris

此代码将安装社区版 DB-API。

或使用特定版本:

pip install https://github.com/intersystems-community/intersystems-irispython/releases/download/3.9.3/intersystems_iris-3.9.3-py3-none-any.whl

然后,您可以使用 DB-API 连接 IRIS 数据库,并执行 SQL 查询或其他任何使用 DB-API 的 Python 代码,如 SQLAlchemy、Django、langchain、pandas 等。

DB-API 使用示例

它的使用方法与其他所有 Python DB-API 相同,示例如下:

# src/python/article/dbapi_community_example.py
import intersystems_iris.dbapi._DBAPI as dbapi

config = {
    "hostname": "localhost",
    "port": 1972,
    "namespace": "USER",
    "username": "_SYSTEM",
    "password": "SYS",
}

with dbapi.connect(**config) as conn:
    with conn.cursor() as cursor:
        cursor.execute("select ? as one, 2 as two", 1)   # second arg is parameter value
        for row in cursor:
            one, two = row
            print(f"one: {one}")
            print(f"two: {two}")

您可以使用任何 Python 解释器运行此脚本:

python3 /irisdev/app/src/python/article/dbapi_community_example.py

也可以使用 sqlalchemy:

from sqlalchemy import create_engine, text

COMMUNITY_DRIVER_URL = "iris://_SYSTEM:SYS@localhost:1972/USER"
OFFICIAL_DRIVER_URL = "iris+intersystems://_SYSTEM:SYS@localhost:1972/USER"
EMBEDDED_PYTHON_DRIVER_URL = "iris+emb:///USER"

def run(driver):
    # Create an engine using the official driver
    engine = create_engine(driver)

    with engine.connect() as connection:
        # Execute a query
        result = connection.execute(text("SELECT 1 AS one, 2 AS two"))

        for row in result:
            print(f"one: {row.one}, two: {row.two}")

if __name__ == "__main__":
    run(OFFICIAL_DRIVER_URL)
    run(COMMUNITY_DRIVER_URL)
    run(EMBEDDED_PYTHON_DRIVER_URL)

您可以使用任何 Python 解释器运行此脚本:

python3 /irisdev/app/src/python/article/dbapi_sqlalchemy_example.py

您将看到如下输出:

one: 1, two: 2
one: 1, two: 2
one: 1, two: 2

优点

  • 更好的支持:对 SQLAlchemy、Django、langchain 以及其他使用 DB-API 的 Python 库提供更好的支持。
  • 依托于社区:它由社区维护,这意味着随着时间的推移,可能会对其进行更新和改进。
  • 兼容性:它兼容官方 InterSystems DB-API,因此您可以在官方版与社区版之间轻松切换。

缺点

  • 速度:社区版的优化程度可能不如正式版高,某些场景下可能会导致速度变慢。

在 IRIS 中调试 Python 代码

在本节中,我们将探讨如何在 IRIS 中调试 Python 代码。

默认情况下,无法在 IRIS 中调试 Python 代码(在包含语言标签或 %SYS.Python 的 objectscript 中),但可以通过社区解决方案在 IRIS 中调试 Python 代码。

使用方法

先安装 IoP 基于 Python 的互操作性

pip install iris-pex-embedded-python
iop --init

此代码将安装 IoP 和新的 ObjectScript 类,以便您可以在 IRIS 中调试 Python 代码。

然后,您可以使用 IOP.Wrapper 类包装 Python 代码并实现调试。

Class Article.DebuggingExample Extends %RegisteredObject
{
ClassMethod Run() As %Status
{
    set myScript = ##class(IOP.Wrapper).Import("my_script", "/irisdev/app/src/python/article/", 55550) // Adjust the path to your module
    Do myScript.run()
    Quit $$$OK
}
}

然后,向 launch.json 文件添加以下配置,将 VsCode 配置为使用 IoP 调试器:

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python in IRIS",
            "type": "python",
            "request": "attach",
            "port": 55550,
            "host": "localhost",
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}/src/python/article",
                    "remoteRoot": "/irisdev/app/src/python/article"
                }
            ]
        }
    ]
}

现在,您可以运行用于导入 Python 模块的 ObjectScript 代码,然后将 VsCode 中的调试器关联到端口 55550

您可以使用以下命令运行此脚本:

iris session iris -U IRISAPP '##class(Article.DebuggingExample).Run()'

然后,您可以在 Python 代码中设置断点,调试器将在这些断点处停止执行,以便您检查变量并单步执行代码。

远程调试实际运作视频(针对 IoP,但原理是相同的):

Python 代码中还提供回溯信息,这对调试非常有用。

启用回溯时:

Traceback enabled

禁用回溯时:

Traceback disabled

优点

  • 远程调试:您可以远程调试在 IRIS 中运行的 Python 代码,在我看来,这是一项革命性的功能。
  • Python 调试功能:您可以使用所有 Python 调试功能,例如断点、变量检查和单步执行代码。
  • 回溯:您可以看到 Python 代码中错误的完整回溯信息,这对调试非常有用。

缺点

  • 设置的复杂程度:设置 IoP 和调试器可能会比较复杂,特别是对于初学者来说。
  • 社区解决方案:该解决方案属于社区解决方案,因此可能不像官方解决方案那样稳定,文档也可能不完善。

结论

总的来说,在 IRIS 中调试 Python 代码可以通过 IoP 社区解决方案来实现,借助该解决方案,您可以使用 Python 调试器调试在 IRIS 中运行的 Python 代码。 不过,此解决方案需要执行一些设置操作,并可能不像官方解决方案一样稳定。

IoP(基于 Python 的互操作性)

在本节中,我们将探讨 IoP(基于 Python 的互操作性)解决方案,利用该解决方案,您可以使用 Python 优先的方式在 IRIS 中运行 Python 代码。

我开发这个解决方案已经有一段时间了,可以说它是我的心血之作,该解决方案尝试解决或改善我们在本系列文章中提到的所有问题。

IoP 的要点:

  • Python 优先:您可以使用 Python 编写应用程序逻辑,这样,您可以利用 Python 的功能和库。
  • IRIS 集成:您可以轻松将 Python 代码与 IRIS 功能相集成。
  • 远程调试:您可以远程调试在 IRIS 中运行的 Python 代码。
  • 回溯:您可以看到 Python 代码中错误的完整回溯信息,这对调试非常有用。
  • 虚拟环境:支持虚拟环境功能,因此您可以更加轻松地管理依赖项。

要详细了解 IoP,您可以查阅官方文档

然后,您可以阅读以下文章详细了解 IoP:

🐍❤️如您所见,通过 IoP 这一功能强大的方法,我们可以将 Python 与 IRIS 相集成,从而可以更轻松地开发和调试应用程序。

您无需继续使用 irispython,也不必手动设置 sys.path,而是可以使用虚拟环境,并且可以调试在 IRIS 中运行的 Python 代码。

结论

希望大家喜欢这一系列关于在 IRIS 中使用 Python 的文章。

如果您对这一系列的文章有任何疑问或反馈,请随时联系我。

祝您在 IRIS 中使用 Python 时一切顺利!

0
0 18
文章 Lilian Huang · 六月 3, 2025 2m read

IRIS 支持开箱即用的 CCDA 和 FHIR 转换,但访问和查看这些功能需要大量的时间设置和产品知识。IRIS Interop DevTools 应用程序旨在弥补这一差距,让实施人员能够立即进入并查看产品的内置转换功能。

除了 IRIS XML、XPath 和 CCDA 转换环境,Interop DevTools 软件包现在还提供:

  • FHIR-SDA 转换设置
  • SDA-FHIR 转换设置
  • 构建 FHIR 错误验证
  • 加载 FHIR 转换所需的内容

已经更新仪表板的外观和感触,看起来更加直观和用户友好。在 IRIS 中执行,以便充分利用环境,同时用户界面允许可见性、可重复性以及隔离修改和模块进行测试的能力。

以下是5个功能支持:

1. XPath 评估器: 根据输入 CCD 评估 XPath 并返回结果

2. CCDA 到 SDA 转换: 通过选定的基本 XSL 转换运行输入的 CCD,并显示 SDA 结果。

3. XSL 模板测试器: 针对输入 CCD 应用单个 XSL 模板,并显示生成的 CCD。

4. FHIR 到 SDA 转换: 在输入的 FHIR 资源或捆绑包上运行标准的 FHIR 到 SDA 转换,并显示 SDA 结果或 FHIR 验证错误响应。

5. 5. SDA 到 FHIR 转换: 在输入的 SDA 消息上运行标准的 SDA 到 FHIR 转换,并显示 FHIR 束结果。

0
0 79
文章 Jeff Liu · 五月 22, 2025 5m read

基于 XSLT 互联互通临床文档到 FHIR 资源转换

国家卫健委互联互通成熟度评测中的临床共享文档,作为医疗信息交换的重要载体,采用了XML标准的文档格式。随着医疗信息化的发展,FHIRFast Healthcare Interoperability Resources)作为新一代医疗信息交换标准,因其简洁性、灵活性和RESTful架构,逐渐成为医疗数据交换的理想选择。将共享文档文档转换为FHIR资源,能够有效促进不同医疗系统间的数据互通,提升医疗信息的利用价值。

XSLT(可扩展样式表语言转换)是一种用于将XML文档转换为其他XML文档或文本格式的语言。在医疗数据转换场景中,XSLT凭借其强大的XML处理能力,成为共享文档FHIR转换的理想工具。

我们知道共享文档文档是一种结构化的XML文档,通常包含以下主要部分:

- 文档头(Document Header):包含文档元数据,如文档类型、创建时间、作者等

- 临床数据部分(Clinical Sections):按章节组织的临床信息,如问题列表、用药记录、检查报告等

- 数据条目(Entries):具体的临床数据项,如诊断、药物、检验结果等

FHIR则采用了资源导向的设计理念,每个临床概念都被建模为独立的资源,通过RESTful API进行访问。FHIR资源具有以下特点:

0
0 57
文章 Nicky Zhu · 十月 10, 2024 7m read

本演示程序用于展示如何采用自定义FHIR profile来验证数据合规性。自定义FHIR实施指南基于FHIR R4版本开发,在本例中实现了对Organization资源的扩展并用于验证数据的合规性。

安装

  1. 通过Git clone下载本项目。
  2. 执行docker-compose up -d构建并启动容器,初次执行时需执行需10~15分钟(视配置变化)。将构建InterSystems IRIS for Health镜像,安装FHIR服务器,导入自定义FHIR规范,使自定义FHIR 规范可用于验证数据。
  3. 在Postman中导入TestCases中的测试用例文件,查看各类FHIR约束的测试效果
  4. 容器启动后可查看自定义IG内容

项目代码结构

FHIRValidation
├─ ExampleIG                        
│  ├─ ig.ini
│  ├─ input
│  │  ├─ fsh
│  │  │  ├─ alias.fsh
│  │  │  ├─ codesystems.fsh
│  │  │  ├─ organization.fsh
│  │  │  └─ valuesets.fsh
│  │  └─ pagecontent
│  │     └─ index.md
│  └─ sushi-config.yaml
├─ README.md
├─ README_zh.md
├─ TestCases
│  └─ FHIR Profile-based Validation  testcases.postman_collection.json
├─ docker-compose.yml
└─ image-iris
   ├─ Dockerfile
   └─ src
      ├─ FullIG
      ├─ IGPackages
      │  ├─ hl7.fhir.uv.extensions.r4#5.1.0.tgz
      │  ├─ hl7.terminology.r4#6.0.2.tgz
      │  └─ package.tgz
      └─ init
         └─ init.sh

ExampleIG

该子目录下的所有文件为本项目所采用的自定义FHIR规范SUSHI源码,供用户定义FHIR规约时参考使用。

TestCases

该子目录下存放基于FHIR REST API的测试用例脚本,需导入到Postman中使用

image-iris

该子目录下存放nterSystems IRIS for Health镜像所需的文件,其中: └─ src ├─ FullIG 该目录中存放SUSHI生成的自定义FHIR IG ├─ IGPackages 该目录中存放自定义FHIR IG的 package 文件 └─ init 该目录中存放IRIS的Docker镜像初始化脚本

FHIR package简介

HL7组织推荐使用实施指南(Implementation Guild)来解释如何使用FHIR规范。除用于开发人员阅读的说明(如html)外,实施指南中通常也包括可直接被机器读取和应用的工件(artifacts),可被用于驱动代码生成和数据验证等任务。
FHIR实施指南采用NPM Package规范管理依赖。指南涉及的所有StructureDefinition,ValueSet等资源将被打包在一块,形成可被FHIR Server用于读取规范,生成客户端代码或执行数据质量校验的资源包。
通过SUSHI工具生成的实施指南中就包含若干package文件。如本例中,image-iris/src/IGPackages/package.tgz即为生成的package包,可被IRIS FHIR Server直接导入使用。应当注意到的是,除核心资源包(如hl7.fhir.r4.core)外,完整的FHIR规范还需要引用术语、扩展等额外的资源包。
目前FHIR规范引用机制的文档尚不完善。如基于R4版的FHIR规范除引用hl7.fhir.r4.core外,还需引用hl7.fhir.uv.extensions.r4#5.1.0hl7.terminology.r4#6.0.2,但这些引用关系在R5版本中方有记录,在R4版文档中并未完整声明,需开发者在开发过程中自行补充。
在本例中这些包已下载在image-iris/src/IGPackages文件夹下,将作为依赖在自定义FHIR实施指南之前加载。

FHIR validation简介

参见FHIR规范Validating Resources一节。FHIR规范已经设计了对数据结构、属性基数、值域、代码绑定和约束等一系列机制在内的数据质量校验机制。HL7组织在FHIR规范中并未强制要求遵循何种强度的质量控制,但建议采用宽进严出的原则处理FHIR数据。
对于保存FHIR资源的FHIR存储库而言,保障FHIR资源的数据质量是使医疗行业具有价值,保障医疗行为安全性的前提条件。因此,在构建基于FHIR存储的数据共享交换方案时,即使不得不保存不满足数据质量要求的数据,也应对其进行校验,标识不符合项,推动数据治理活动的进行,从而保障医疗安全和数据消费者的利益。
在FHIR规范指出的多种数据校验方式中,FHIR Validator和FHIR操作对数据质量校验的覆盖最为全面。
本例将使用InterSystems IRIS for Health所提供的$validate操作,通过profile参数对尚未保存的FHIR数据进行校验。使用者也可修改测试用例,构建HTTP POST参数,对存量FHIR资源进行校验。
还应当注意的是,$validate操作如被正确调用,将通过Http 200返回校验结果,如有不符合项,将在返回的OperationOutcome资源中包裹错误信息,而不通过Http代码标识错误。

对FHIR的扩展

在本例中基于FHIR R4对Organization资源进行了如下扩展:

1. 修改language的绑定强度

将机构主要语言的绑定强度修改为required

2. active字段基数从0..1改为1..1

从而使得数据的状态成为必填字段,有且只有一个元素

3. name字段基数从0..1改为1..1

组织机构名称成为必填字段,有且只有一个元素。参考我国医院除医院名称外,如果具备急救中心、胸痛中心等牌照,还可能具有多个名称。但因注意到,这些牌照通常标识了医疗机构提供的服务能力,而非在组织机构注册系统中具备的法定名称,且此类牌照生命周期与医疗机构自身的生命周期并不一致。因此,从牌照获得的名称宜视为该医疗机构的服务能力而非机构的唯一名称。在FHIR中,通过服务能力获得的名称可通过资源HealthcareService提供,该资源与Organization资源间可建立多对一的引用关系,更适合用来表达上述概念。

4. 增加医疗机构的组织机构类型

根据中国国家标准GB/T 20091-2021 组织机构类型,分别增加了CodeSystem organizationtype-code-system和ValueSet organizationtype-vs,并通过Extension向Organization资源中添加了扩展mdm-organizationTypeExtension,从而使得该资源可用于表示表示标识中国组织机构类型。
该扩展通过对Extension切片实现,且基数为1..1,即医疗机构资源必须具有组织机构类型元素。

5. 约束医疗机构证件号码

FHIR基础标准并未纳入中国组织机构统一社会信用代码的证件类型,为此增加了CodeSystem cs-identifierType-code-system,并对Identifier按其类型进行了切片,使之必须可以表达社会信用代码。且社会信用代码的格式遵循以下约束:

  1. identifier.use必须取值为official,即正式/官方用途
  2. identifier.type必须遵循cs-identifierType-code-system要求,system必须为该codesystem的uri,code必须为“USCC”
  3. identifier.value必须遵循自定义约束uscc-length-18,该字段长度必须为18位,其中前17位必须为数字,最后1位必须为数字或字母

测试用例列表

1. Without profile - All OK

未声明资源对应的profile,因此FHIR Server将不对资源中各属性的值进行校验,仅返回All OK。

2. Unknow field

在资源中加入了未被定义的属性isNational,因此校验引擎返回了Unrecognized element错误。

3. Wrong cardinality - less

在本IG中,修改了Organization资源name属性的基数为1..1,即应有且仅有一个组织机构名称。本测试用例未填写名称,因此数据校验失败。 另外,可以观察到Identifier.type经过扩展,加入了统一社会信用代码作为标识符类型,FHIR R4规范里并不包含这个值,但该字段的代码绑定强度仅为example,不强制约束。因此校验引擎返回了information级的值域代码不符合信息而没有报错。

4. Binding strength

在本IG中,组织机构的language属性的代码绑定强度改为了required,则该字段值域必须符合http://hl7.org/fhir/ValueSet/languages,因此,当该字段取值为wrong language时,因不在required值域中,将导致error级错误

5. Wrong value

在本IG中,组织机构类型的值域来自于organizationtype-code-system,因此,当类型为mdm-organizationTypeExtension的extension元素中code的值为“999”,不在值域中时,将导致error级错误

6. Failing invariant

在本IG中,组织机构的社会信用代码必须遵循自定义约束uscc-length-18(该字段长度必须为18位,其中前17位必须为数字,最后1位必须为数字或字母),因此,当其末位为字符“%”时,违反该约束,将导致error级错误

7. Failing profile

对于一个资源定义的一个profile包含了多个约束,因此,在校验时所有不满足profile的问题都将被检出,例如本例中:

  1. 错误的language代码
  2. 错误的组织机构类型
  3. 缺少name字段 可见上述问题都被检出
0
0 127
文章 Jingwei Wang · 六月 23, 2024 8m read

低代码挑战

想象一下那个场景。您正在 Widgets Direct 愉快地工作,这是互联网上首屈一指的小部件和小部件配件零售商。您的老板有一些毁灭性的消息,一些客户可能对他们的小部件不太满意,我们需要一个帮助台应用程序来跟踪这些投诉。为了让事情变得有趣,他希望代码占用非常小,并挑战您使用 InterSystems IRIS 以少于 150 行代码交付应用程序。这可能吗?

免责声明:本文记录了一个非常基本的应用程序的构建,为了简洁起见,省略了安全性和错误处理等细节。该应用程序仅供参考,不得用于任何生产应用。本文使用IRIS 2023.1作为数据平台,并非所描述的所有功能在早期版本中都可用

第 1 步 - 定义数据模型

我们首先定义一个新的干净的命名空间 - 带有代码和数据数据库。虽然所有内容都可以位于 1 个数据库中,但将它们拆分以便于数据刷新。

我们的帮助台系统需要 3 个基本类:一个 Ticket 对象,它可以包含用于记录员工顾问 UserAccount 和客户联系人 UserAccount 之间交互的操作。让我们用一些基本属性来定义它们:

0
0 127
文章 Louis Lu · 六月 11, 2024 8m read

这篇文章介绍了使用由支持 langchain 框架的IRIS来实现问答聊天机器人,其重点介绍了检索增强生成(RAG)。

文章探讨了IRIS中的向量搜索如何在langchain-iris中完成数据的存储、检索和语义搜索,从而实现对用户查询的精确、快速的响应。通过无缝集成以及索引和检索/生成等流程,由IRIS驱动的RAG应用程序使InterSystems开发者能够利用GenAI系统的能力。

为了帮助读者巩固这些概念,文章提供了Jupyter notebook一个完整的问答聊天机器人应用程序,以供参考。

什么是RAG以及它在问答聊天机器人中的角色

RAG,即检索增强生成,是一种通过整合超出初始训练集的补充数据来丰富语言模型(LLM)知识库的技术。尽管LLM在跨不同主题进行推理方面具有能力,但它们仅限于在特定截止日期之前训练的公共数据。为了使AI应用程序能够有效处理私有或更近期的数据,RAG通过按需补充特定信息来增强模型的知识。这是一种替代微调LLM的方法,微调可能会很昂贵。

在问答聊天机器人领域,RAG在处理非结构化数据查询中发挥着关键作用,包括两个主要组成部分:索引和检索/生成。

索引从数据源摄取数据开始,然后将其分割成更小、更易于管理的块以进行高效处理。这些分割的块随后被存储和索引,通常使用嵌入模型和向量数据库,确保在运行时能够快速准确地检索。

0
0 321
文章 Michael Lei · 三月 27, 2023 11m read

好不好玩,能当真吗?


最近几个月,大型语言模型GPT正在激起一些现象。因此,上周末我不可避免地也在玩 ChatGPT,以探究它是否会成为我正在敲打的一些基于 BERT 的“传统”AI 聊天机器人的补充,或者更确切地说,它是否会淘汰它们。
玩的时候脑子里冒出一个念头。通过略微理论化或哲学化,最终互操作性标准(如 HL7 和 FHIR 等)是一种“语言”,对吗? HL7 有自己的语法、规则、词汇甚至方言——每个系统都有自己的语调。这就是为什么当一台机器与另一台机器对话时,它们需要翻译器(例如 DTL 转换)来实现相互理解。
所以环顾四周,似乎一切都是语言:编码是语言:python,javascript和COS也是语言。 HL7、FHIR 甚至 XML 或 JSON 都是语言,只是它们比自然语言更结构化,那么 GPT 应该更容易上手吗?
那么,我们可以从简单地重用 GPT 的预训练编码语言模型来模拟 DTL 开始吗?我们还没有进行调整,以下是初步结果:


测试


模式:完整
型号:code-davinci-002
温度:0
最大长度:1989
其他:默认
停止序列:###
1、将HL7 ADT A01转A05?

输入:  

0
0 237
文章 water huang · 九月 25, 2022 25m read

日常工作中,JSON使用越来越多,很多其他的语言里面有成熟的JSON API,但cache 我一直没找到。而同事们使用的基本都是自己写的一些JSON工具。这些小工具,多多少少有些局限。使用ensemble2016后,发现了Ens.Util.JSON类,他可以处理JSON。翻阅代码后,实际上主要API是 %ZEN.Auxiliary.abstractController。在使用中,我们遇到一个问题,那就是有些JSON的节点名是带有下划线的,这个不太好处理。一般来说,我们把对象转为xml的时候,对象的属性我们是去掉下划线的,带下划线的名字是用 XMLNAME来设置的,于是考虑JSON里面也使用它(如果配置了)作为对象转JSON的时候,JSON的名字。修改 %ZEN.Auxiliary.jsonProvider的 %WriteJSONStreamFromObject方法后,发现ensemble的一些自带的页面的功能受到了影响。因此重新建立了一个副本。这里假定为 Util.JSON Extends %ZEN.Auxiliary.abstractController [ System = 3 ]。对象转为JSON的方法中,需要修改 ClassMethod %ObjectToJSON(pObject As %RegisteredObject, ByRef pVisited, pLevel 

4
0 269
文章 王喆 👀 · 九月 7, 2022 4m read

前言

自接触IRIS以来在医院之中使用最多的是其作为一个ESB集成引擎来使用,作为医院的集成平台+数据中心中的一环。但是其也可以进行CRUD作为前后端分离项目的后端使用:

文章目录

前言

文章目录

一、开发技术和工具

二、开发路径和相关代码

       1.数据库

       2.准备工作

       3.开发接口的相关步骤

       4.成果展示

总结

本文章将以一个简单的页面展示IRIS的相关CRUD操作:

是的,我计划使用IRIS重构Production页面,先展示一下我开发完的成果吧:

呃の好像是一毛一样,哈哈

一、开发技术和工具

工欲善其事,比先利其器:

后端:HealthConnect+Global
前端:vue

使用IRIS%CSP.REST开发的Rest API接口,前端使用vue简单的把接口相关内容展示,可以编辑、新增组件,控制production启动、停止、更新检测状态。

二、开发路径和相关代码

1.数据库

首先,我们找到Production的相关表,分别是:Ens_Config.Item Ens_Config.Production 如图所示:

所以代码的思路很明确了对Ens_Config.Item进行新增、删除、修改、查询操作对Ens_Config.Production进行查询操作。

2.准备工作

在正式开发之前我们得先做一些准备工作:比如开放网关

7
6 625
文章 Michael Lei · 九月 15, 2022 6m read

如果你读了我之前介绍QEWD微服务的文章,希望你会渴望了解如何使用它们。  所以在这篇文章中,我将解释你需要知道的东西,以便开始使用。

如果你在QEWD资源库中,你会发现目录: 

  https://github.com/robtweed/qewd/blob/master/example/jwt

在我之前关于JSON网络令牌(JWTs)和QEWD的文章中,我用这个示例应用程序来解释如何使用JWTs。  这个示例应用程序还演示了如何设置一个简单的微服务,在这种情况下是一个处理用户认证的服务。  所以,现在让我深入了解一下这个例子应用程序的这方面内容。

如果你想使用QEWD微服务,你也必须使用JWTs--它们提供了一种方法,用户的认证和会话可以被多个独立的QEWD服务器交叉通信和处理。  因此,请看一下启动文件:

   https://github.com/robtweed/qewd/blob/master/example/jwt/startup_file/q…

你会看到,它将QEWD配置为使用JWT,并定义了用于签署和加密JWT的加密字符串:

           jwt: {
            secret: 'someSecret123'
          },

而且,你可以看到如何定义微服务:

0
0 189
文章 姚 鑫 · 六月 5, 2021 5m read

第七章 Caché JSON %JSON快速参考

%JSON快速参考

本节提供本章中讨论的%JSON方法、属性和参数的快速参考。

%JSON.Adaptor方法

这些方法提供了从JSON序列化和序列化到JSON的能力。

%JSONExport()

%JSON.Adaptor.%JSONExport()将启用JSON的类序列化为JSON文档,并将其写入当前设备。

   method %JSONExport(%mappingName As %String = "") as %Status
  • %mappingName(可选)-要用于导出的映射的名称。基本映射由"" 表示,并且是默认映射。

%JSONExportToStream()

%JSON.Adaptor.%JSONExportToStream()将启用`JSON的类序列化为JSON文档并将其写入流。

   method %JSONExportToStream(ByRef export As %Stream.Object, 
      %mappingName As %String = "") as %Status
  • export - 包含序列化的JSON文档的导出流。
  • %mappingName(可选)-要用于导出的映射的名称。基本映射由""表示,并且是默认映射。

%JSONExportToString()

%JSON.Adaptor.%JSONExportToString()将启用JSON的类序列化为JSON文档,并将其作为字符串返回。

   method %JSONExportToString(ByRef %export As %String, 
      %mappingName As %String = "") as %Status
  • Export-包含序列化的JSON文档的字符串。
  • %mappingName可选)-要用于导出的映射的名称。基本映射由""表示,并且是默认映射。

%JSONImport()

%JSON.Adaptor.%JSONImport()将JSON或动态实体输入导入此对象。

   method %JSONImport(input, %mappingName As %String = "") as %Status
  • input -JSON可以是字符串或流,也可以是%DynamicAbstractObject的子类。
  • %mappingName(可选)-要用于导入的映射的名称。基本映射由""表示,并且是默认映射。

%JSONNew()

%JSON.Adaptor.%JSONNew()获取启用JSON的类的实例。在返回此类的实例之前,可以重写此方法以执行自定义处理(如初始化对象实例)。但是,不应直接从用户代码调用此方法。

   classmethod %JSONNew(dynamicObject As %DynamicObject, 
      containerOref As %RegisteredObject = "") as %RegisteredObject
  • dynamicObject -具有要分配给新对象的值的动态实体。
  • containerOref (可选)-从%JSONImport()调用时的包含对象实例。

%JSON.Adaptor类和属性参数

除非另有说明,否则可以为类或单个属性指定参数。作为类参数,它指定相应属性参数的默认值。作为属性参数,它指定覆盖默认值的值。

%JSONENABLED

启用属性转换方法的生成。

  parameter %JSONENABLED = 1;
  • 1-(默认)将生成JSON启用方法。
  • 0-方法生成器不会生成Runnable方法。

%JSONFIELDNAME (properties only)

设置要用作JSON内容中字段名的字符串。

  parameter %JSONFIELDNAME

默认情况下,使用属性名称。

%JSONIGNOREINVALIDFIELD

控制对JSON输入中意外字段的处理。

  parameter %JSONIGNOREINVALIDFIELD = 0;
  • 0-(默认值)将意外字段视为错误。
  • 1-意外字段将被忽略。

%JSONIGNORENULL

指定如何存储字符串属性的空字符串。此参数仅适用于真字符串(由 XSDTYPE = "string"JSONTYPE="string"确定)。

  parameter %JSONIGNORENULL = 0;
  • 0-(默认)JSON输入中的空字符串存储为$char(0)$char(0)作为字符串""写入JSON。JSON输入中缺少的字段始终存储为"",并且根据%JSONNULL参数,""始终输出到JSON。
  • 1-空字符串和缺少的JSON字段都作为""输入,而""$char(0)都作为字段值""输出。

%JSONINCLUDE (properties only)

指定此属性是否包含在JSON输出或输入中。

  parameter %JSONINCLUDE = "inout"
  • "inout"(默认)-在输入和输出中都包含。
  • "outputonly" -忽略该属性作为输入。
  • "inputOnly" -忽略该属性作为输出。
  • “none”—从不包含该属性。

%JSONNULL

控制未指定属性的处理。

  parameter %JSONNULL = 0;
  • 0 -(默认)在导出期间跳过与未指定属性对应的字段。
  • 1 -未指定的属性作为空值导出。

%JSONREFERENCE

指定如何将对象引用投影到JSON字段。

  parameter %JSONREFERENCE = "OBJECT";
  • "OBJECT" -(默认)被引用类的属性用来表示被引用的对象。
  • “ID”-持久或串行类的ID用于表示引用。
  • “OID”——持久类或串行类的OID用于表示引用。 oidclassname,id的形式投射到JSON中。 -"GUID" -持久化类的GUID用来表示引用。

%JSON.ForMatter方法和属性

%JSON.ForMatter类可用于格式化%DynamicAbstractObject子类的JSON字符串、流或对象。

Format()

%JSON.Formatter.Format()使用指定的缩进格式化JSON文档并将其写入当前设备。

method Format(input) as %Status
  • input -JSON可以是字符串或流,也可以是%DynamicAbstractObject的子类。

FormatToStream()

%JSON.Formatter.FormatToStream()使用指定的缩进格式化JSON文档并将其写入流。

method FormatToStream(input, ByRef export As %Stream.Object) as %Status
  • input -JSON可以是字符串或流,也可以是%DynamicAbstractObject的子类。
  • export -格式化的JSON流。

FormatToString()

%JSON.Formatter.FormatToString()使用指定的缩进格式化JSON文档并将其写入字符串,或将启用JSON的类序列化为JSON文档并将其作为字符串返回。

method FormatToString(input, ByRef export As %String = "") as %Status
  • input-JSON可以是字符串或流,也可以是%DynamicAbstractObject的子类。
  • export (可选)-格式化的JSON流。

Indent

%JSON.Formatter.Indent属性指定是否应缩进JSON输出。默认为true。

property Indent as %Boolean [ InitialExpression = 1 ];

IndentChars

%JSON.Formatter.IndentChars属性指定在启用缩进时用于每个缩进级别的字符序列。默认为一个空格。

property IndentChars as %String [ InitialExpression = " " ];

LineTerminator

%JSON.Formatter.LineTerminator属性指定缩进时终止每行的字符序列。默认为$char(13,10)


property LineTerminator as %String [ InitialExpression = $char(13,10) ];
/// d ##class(PHA.TEST.Xml).Obj2FormatterJson()
ClassMethod Obj2FormatterJson()
{
	s event = ##class(Model.Event).%New()
	s event.Name = "yx"
	s location = ##class(Model.Location).%New()
	s location.City = "tianjin"
	s location.Country = "china"
	s event.Location = location
	d event.%JSONExportToString(.jsonEvent)
	s formatter = ##class(%JSON.Formatter).%New()
	s formatter.Indent = 1
	s formatter.IndentChars = " - "
	//s formatter.LineTerminator = "!"
	d formatter.Format(jsonEvent)
}
DHC-APP>d ##class(PHA.TEST.Xml).Obj2FormatterJson()
{
 - "Name":"yx",
 - "Location":{
 -  - "City":"tianjin",
 -  - "Country":"china"
 - }
}
DHC-APP>d ##class(PHA.TEST.Xml).Obj2FormatterJson()
{! - "Name":"yx",! - "Location":{! -  - "City":"tianjin",! -  - "Country":"china"! - }!}
0
0 280
文章 姚 鑫 · 六月 5, 2021 7m read

第六章 Caché JSON 使用JSON适配器

JSON适配器是一种将ObjectScript对象(registered, serial or persistent)映射到JSON文本或动态实体的方法。本章涵盖以下主题:

  • 导出和导入-介绍启用JSON的对象并演示%JSON.Adaptor导入和导出方法
  • 带参数映射-描述控制如何将对象属性转换为JSON字段的属性参数。
  • 使用扩展数据映射块-介绍将多个参数映射应用到单个类的方法。
  • 格式化JSON-演示如何使用%JSON.ForMatter格式化JSON字符串。
  • %JSON快速参考-提供本章中讨论的每个%JSON类成员的简要说明。

Exporting and Importing

从JSON序列化或序列化到JSON的任何类都需要子类%JSON.Adaptor,它包括以下方法:

  • %JSONExport()将启用JSON的类序列化为JSON文档,并将其写入当前设备。
  • %JSONExportToStream()将启用JSON的类序列化为JSON文档并将其写入流。
  • %JSONExportToString()将启用JSON的类序列化为JSON文档并将其作为字符串返回。
  • %JSONImport()将JSON作为字符串或流导入,或者作为%DynamicAbstractObject的子类导入,并返回启用JSON的类的实例。

为了演示这些方法,本节中的示例将使用这两个类:

启用JSON的类Model.EventModel.Location

  Class Model.Event Extends (%Persistent, %JSON.Adaptor)
  {
    Property Name As %String;
    Property Location As Model.Location;
  }
  Class Model.Location Extends (%Persistent, %JSON.Adaptor)
  {
    Property City As %String;
    Property Country As %String;
  }

如所见,我们有一个持久Event类,有一个Location属性类型为Model.Location。这两个类都继承自%JSON.Adaptor。这使能够填充对象图,并将其直接导出为JSON字符串。

将对象导出为JSON字符串


/// d ##class(PHA.TEST.Xml).SaveEvent()
ClassMethod SaveEvent()
{
	set event = ##class(Model.Event).%New()
	set event.Name = "Global Summit"
	set location = ##class(Model.Location).%New()
	set location.Country = "United States of America"
	set event.Location = location
	do event.%JSONExport()
}

此代码显示以下JSON字符串:

DHC-APP>d ##class(PHA.TEST.Xml).SaveEvent()
{"Name":"Global Summit","Location":{"Country":"United States of America"}}

可以使用%JSONExportToString()而不是%JSONExport()JSON字符串赋给变量:

/// d ##class(PHA.TEST.Xml).SaveEventString()
ClassMethod SaveEventString()
{
	set event = ##class(Model.Event).%New()
	set event.Name = "Global Summit"
	set location = ##class(Model.Location).%New()
	set location.Country = "United States of America"
	set event.Location = location
	do event.%JSONExportToString(.jsonEvent)
	w jsonEvent,!
}
DHC-APP>d ##class(PHA.TEST.Xml).SaveEventString()
{"Name":"Global Summit","Location":{"Country":"United States of America"}}

最后,可以通过%JSONImport()将JSON字符串转换回对象。此示例从上一个示例中获取字符串变量jsonEvent,并将其转换回Model.Event对象:

将JSON字符串导入到对象中

/// d ##class(PHA.TEST.Xml).SaveEventStringImport()
ClassMethod SaveEventStringImport()
{
	set event = ##class(Model.Event).%New()
	set event.Name = "yx"
	set location = ##class(Model.Location).%New()
	s location.City = "tianjin"
	set location.Country = "United States of America"
	set event.Location = location
	do event.%JSONExportToString(.jsonEvent)
	
	
	set eventTwo = ##class(Model.Event).%New()
	do eventTwo.%JSONImport(jsonEvent)
	write eventTwo.Name,!,eventTwo.Location.City
}
DHC-APP>d ##class(PHA.TEST.Xml).SaveEventStringImport()
yx
tianjin

导入和导出都适用于任意嵌套的结构。

使用参数映射

可以通过设置相应的参数为每个单独的属性指定映射逻辑。

可以通过指定属性参数来更改Model.Event类(在上一节中定义)的映射:

  Class Model.Event Extends (%Persistent, %JSON.Adaptor)
  {
    Property Name As %String(%JSONFIELDNAME = "eventName");
    Property Location As Model.Location(%JSONINCLUDE = "INPUTONLY");
  }

此映射引入了两个更改:

  • 属性名称将映射到名为eventName的JSON字段。
  • Location属性仍将由%JSONImport()用作输入,但将被%JSONExport()和其他导出方法忽略。

以前,在未修改的Model.Event类的实例上调用%JSONExport(),并返回以下JSON字符串:

  {"Name":"Global Summit","Location":{"City":"Boston","Country":"United States of America"}}

如果对重新映射的Model.Event实例调用%JSONExport()(使用相同的属性值),将返回以下字符串:

DHC-APP>d ##class(PHA.TEST.Xml).SaveEvent()
{"eventName":"Global Summit"}

有各种参数可用于调整映射:

  • %JSONFIELDNAME(仅限属性)设置要用作JSON内容中的字段名称的字符串(默认情况下,值为属性名称)。
  • %JSONIGNOREINVALIDFIELD控制对JSON输入中意外字段的处理。
  • %JSONIGNORENULL允许开发人员覆盖字符串属性的空字符串的默认处理。
  • %JSONINCLUDE(仅限属性)指定该属性是否包含在JSON输出或输入中(有效值为"inout"(默认),"outputonly""inputOnly",或"none")。
  • %JSONNULL指定了如何为字符串属性存储空字符串。
  • %JSONREFERENCE指定如何将对象引用投影到JSON字段。 选项包括OBJECT(默认值)IDOIDGUID

使用XData映射块

可以在特殊的XData mapping块中指定映射,并在调用导入或导出方法时应用映射,而不是在属性级别上设置映射参数。

Class Model.Event Extends (%Persistent, %JSON.Adaptor)
{

Property Name As %String;

Property Location As Model.Location;

XData OnlyLowercaseTopLevel
{
<Mapping xmlns="http://www.intersystems.com/jsonmapping">
        <Property Name="Name" FieldName="eventName"/>
        <Property Name="Location" Include="INPUTONLY"/>
      </Mapping>
}

Storage Default
{
<Data name="EventDefaultData">
<Value name="1">
<Value>%%CLASSNAME</Value>
</Value>
<Value name="2">
<Value>Name</Value>
</Value>
<Value name="3">
<Value>Location</Value>
</Value>
</Data>
<DataLocation>^Model.EventD</DataLocation>
<DefaultData>EventDefaultData</DefaultData>
<IdLocation>^Model.EventD</IdLocation>
<IndexLocation>^Model.EventI</IndexLocation>
<StreamLocation>^Model.EventS</StreamLocation>
<Type>%Storage.Persistent</Type>
}

}

这里有一个重要的区别:XData块中的JSON映射不会改变默认行为,但是可以通过在导入和导出方法的可选参数%mappingName中指定块名称来应用它们。 例如:

  do event.%JSONExport("OnlyLowercaseTopLevel")
/// d ##class(PHA.TEST.Xml).SaveEventXData()
ClassMethod SaveEventXData()
{
	set event = ##class(Model.Event).%New()
	set event.Name = "Global Summit"
	set location = ##class(Model.Location).%New()
	set location.Country = "United States of America"
	set event.Location = location
	do event.%JSONExport("OnlyLowercaseTopLevel")
}

显示

  {"eventName":"Global Summit"}

就像在属性定义中指定了参数一样。

如果没有具有提供名称的扩展数据块,将使用默认映射。使用这种方法,可以配置多个映射并分别引用每个调用所需的映射,从而使可以更好地控制,同时使您的映射更加灵活和可重用。

定义到扩展数据映射块

支持JSON的类可以定义任意数量的附加映射。每个映射都在以下形式的单独扩展数据块中定义:

  XData {MappingName}
  {
    <Mapping  {ClassAttribute}="value" [...] xmlns="http://www.intersystems.com/jsonmapping".>
      <{Property Name}="PropertyName" {PropertyAttribute}="value" [...] />
      [... more Property elements]
    </Mapping>
  }

其中,{MappingName}{ClassAttribute}{Property Name}{PropertyAttribute}的定义如下:

  • MappingName%JSONREFERENCE参数或Reference属性使用的映射的名称。
  • ClassAttribute 指定映射的类参数。可以定义以下类属性:
    • Mapping -要应用的扩展数据映射块的名称。
    • IgnoreInvalidField-指定类参数%JSONIGNOREINVALIDFIELD
    • NULL-指定类参数%JSONNULL
    • IgnoreNull-指定类参数%JSONIGNORENULL
    • Reference -指定类参数%JSONREFERENCE
  • PropertyName 要映射的属性的名称。
  • PropertyAttribute 指定映射的特性参数。可以定义以下特性属性:
    • FieldName-指定属性参数%JSONFIELDNAME(默认情况下与属性名称相同)。
    • Include -指定属性参数%JSONINCLUDE(有效值为"inout"(默认值),"outputonly""inputOnly",或"none")。
    • Mapping -要应用于对象属性的映射定义的名称。
    • NULL-覆盖类参数%JSONNULL
    • IgnoreNull-覆盖类参数%JSONIGNORENULL
    • Reference -覆盖类参数%JSONREFERENCE

格式化JSON

%JSON.ForMatter是一个具有非常简单接口的类,允许将动态对象、数组和JSON字符串格式化为更易于阅读的表示形式。所有方法都是实例方法,因此始终从检索实例开始:

  set formatter = ##class(%JSON.Formatter).%New()

此选择背后的原因是,可以将格式化程序配置为只使用一次某些字符作为行终止符和缩进(例如,空格与制表符;请参阅本节末尾的属性列表),然后在需要的任何地方使用它。

Format()方法接受动态实体或JSON字符串。下面是一个使用动态对象的简单示例:

/// d ##class(PHA.TEST.Xml).FormatterJson()
ClassMethod FormatterJson()
{
	s formatter = ##class(%JSON.Formatter).%New()
	s dynObj = {"type":"string"}
	do formatter.Format(dynObj)
}
DHC-APP>d ##class(PHA.TEST.Xml).FormatterJson()
{
  "type":"string"
}

Format方法可以将输出定向到当前设备、字符串或流:

  • Format()使用指定的缩进格式化JSON文档并将其写入当前设备。
  • FormatToStream()使用指定的缩进格式化JSON文档并将其写入流。
  • FormatToString()使用指定的缩进格式化JSON文档并将其写入字符串,或者将启用JSON的类序列化为JSON文档并将其作为字符串返回。

此外,以下属性可用于控制缩进和换行符:

  • Indent 缩进指定是否应缩进JSON输出
  • IndentChars 指定用于每个缩进级别的字符序列(默认为每个级别一个空格)。
  • LineTerminator 行终止符指定缩进时终止每行的字符序列。
0
0 226
文章 Hao Ma · 六月 4, 2021 3m read

之所以称为Dynamic,是说这个对象在代码编译的时候可以不定义对象的属性和结构,在runtime时才根据装入的数据来产生对象定义。IRIS里用Dynamic Object来处理JSON数据。简单说: 先定义一个Dynamic Object, 把JSON数据装进去,然后用对象的方式处理JSON文档。

让我们看看是它是怎么工作的。

创建一个Dynamic Object很简单, 标准而且啰嗦的写法是:

set dynObject1 = ##class(%DynamicObject).%New()

大家通常用简单的写法,像这样用一个{}来定义Dynamic Object:

DEMO>set dynObject1 = {}
DEMO>zw dynObject1
dynObject1={}  ; <DYNAMIC OBJECT>

字符串,流到DynamicObject的导入导出

把JSON数据从字符串或者流导入DynamicObject被称作Deserializing;反之,把DynamicObject里的JSON导出来到String或者Stream叫Serializing。在类%DynmicObject中用的是%FromJSON()和%ToJSON()两个方法,一个是类方法,一个是实例方法:

//从字符串,流里导入数据到%DynmicObject。str可以是字符串,stream, 或者流文件的URL
ClassMethod %FromJSON(str)  

//从%DynmicObject导出到字符串(出参),或者流(outStrm);是的,我知道这样method很别扭
Method %ToJSON(outStrm As %Stream.Object) As %String

实际使用的例子是这样:

DEMO>set str="{""aNumber"":42,""aDate"":""2021-06-04""}"
DEMO>set dynObject2=##class(%DynamicObject).%FromJSON(str) 
DEMO>zw dynObject2
dynObject2={"aNumber":42,"aDate":"2021-06-04"}  ; <DYNAMIC OBJECT>

上面的例子很傻。既然字符串是定义好的,就没必要先赋值给一个变量,直接用下面的代码就创建了一个Dynamic Object, 省掉了赋值字符串变量里用到的来escape符号"。

set dynObject2 = {"aNumber":42,"aDate":"2021-06-04"}

接着, 我们来看看真正体现Dynamic的对象定义。下面的代码中传入一个String消息,那么就需要用%FromJSON()来把JSON装入DynamicObject

method example1(pInput As %Ens.StringRequest) As %Status
{   //把请求里的JSON字符串装入DynamicObject
    set dynObject2=##class(%DynamicObject).%FromJSON(pInput.StringValue)
    
    //把DynamicObject导出到字符串
    set myString= dynObject2.%ToJSON()
    //把DynamicObject导出到流
    set myStream=##class(%Stream.GlobalCharacter).%New()
    do dynObject2.%ToJSON(myStream)

    //打印结果
    w dynObject2.%ClassName(),!
    w mystring, !
    w myStream.Read(),!
}

以上代码的执行结果是:

%DynamicObject
{"aNumber":42,"aDate":"2021-06-04"}
{"aNumber":42,"aDate":"2021-06-04"}

用DynamicObject处理JSON

把JSON数据装入DynamicObject是为了方便用对象的方式处理JSON,比如取值:

w dynObject2.aName,!
w dynObject.aDate,!

或者,给对象的属性赋值

set dynObject2.aNumber=43
set dynObject2.aDate=$ZD($H)

或者,也可以使用%Get(), %Set()方法,根据key,得到或者修改value,比如

set tempVariable = dynObject2.%Get("aNumber")
do dynObject2.%Set("aNumber",44)

关于%Get(),%Set()还有些复杂的用法,请参考在线文档。

下面是最重要的, 遍历整个DynamicObject

set itr = dynObject2.%GetIterator()
while itr.%GetNext(.key,.val) 
{   
    write key_", "
    write val_", "
    write dynObject2.%GetTypeOf(key),!
} 

其他的方法包括: %Remove(), %IsDefined(), 请自己查阅文档。

DynamicObject的错误处理

Dynamic Object不使用的%Status来做错误处理,因此我们必须用try-catch来捕捉错误,比如下面的代码:

TRY { set invalidObject = {}.%FromJSON("{:}") } CATCH errobj { write errobj.Name_", "errobj.Location", error code "_errobj.Code,! RETURN }

其他的几点注意

有了上面的知识,已经足以在代码中处理JSON文档了。还有几点请注意:

  • %Library.DynamicArray

和DynamicObject的区别是这是一个Array, 用法很类似,请各位自己阅读文档了解。

  • Dynamic Object可以嵌套, 比如这样 set objectStr = {"a":12,"b":"some string","c":{"x":1,"y":2}}

  • 定义Dynamic Object时可以使用ObjectScirpt表达式,比如

      set dynObj = { "Date":($ZD($H,3)) }
    

下篇文章我会介绍%JSON.Adapter

0
0 285
文章 Hao Ma · 一月 15, 2021 6m read

假设您想编写一些真正的web应用程序,例如medium.com网站的简单克隆。这类应用程序可以在后端使用任何不同的语言编写,也可以使用前端的任何框架编写。编写这样一个应用程序有很多方法,你也可以看看这个项目。它为完全相同的应用程序提供了一堆前端和后端实现。您可以轻松组合它们,任何所选前端应该与任何后端搭配。

我来介绍一下这个使用后端InterSystems IRIS来实现后端的相同的应用程序。  

0
0 259
文章 Stefan Wittmann · 一月 4, 2021 4m read

最近我一直在发布对 JSON 功能的一些更新,我很高兴许多人提供了反馈。 今天,我想将重点放在另一个方面:使用 SQL 查询生成 JSON。

显然,SQL 是从关系模型中检索记录的重要概念。 假设你要查询数据并将其以简单 JSON 结构的形式公开给 REST 服务, 通常,你必须查询数据,然后对结果集进行遍历,最后为每条记录构造一个 JSON 结构。 你需要编写自定义代码。

我们添加了标准 SQL 函数 JSON_OBJECTJSON_ARRAY,这样可以更容易地直接从 SQL 查询生成 JSON,而无需编写代码。 这两个函数是 Caché 2016.2 开始新增的。

以下表 Baking.Pastry 为例:

<td>
  类型
</td>

<td>
  说明
</td>
<td>
  Choux Pastry
</td>

<td>
  A light pastry that is often filled with cream
</td>
<td>
  Puff Pastry
</td>

<td>
  Many layers that cause the pastry to expand or puff when baked
</td>
<td>
  Filo Pastry
</td>

<td>
  Multiple layers of a paper-thin dough wrapped around a filling
</td>
1
2
3

JSON_OBJECT

JSON_OBJECT 是一个接受多个键值对并生成 JSON 对象的函数。

SELECT JSON_OBJECT('pastry_type' : Type, 'pastry_description' : Description) AS pastryJSON FROM Baking.Pastry

Row    pastryJSON

----   ---------------

1      {"pastry_type" : "Choux Pastry", "pastry_description" : "A light pastry that is often filled with cream"}

2      {"pastry_type" : "Puff Pastry", "pastry_description" : "Many layers that cause the pastry to expand or puff when baked"}

3      {"pastry_type" : "Filo Pastry", "pastry_description" : " Multiple layers of a paper-thin dough wrapped around a filling"}

在此例中,JSON_OBJECT 函数为每条记录生成一个 JSON 对象。 每个对象都包含两个属性 pastry_typepastry_description,因为我们为函数提供了两个参数。 每个参数由两部分组成,用冒号分隔:

  1. 应注入到JSON对象中的键的名称
  2. 与该键关联的字段值

此示例设置了静态键,因为我只提供了字符串文字,例如“pastry_type”。 对于字段值(我指的是列),例如 Type,无论该列的内容是什么,都将设置为关联键的值。 这是构造 JSON 对象的常见用例,但是通过为键传递列,还可以动态创建键。

JSON_ARRAY

JSON_ARRAY 的工作方式类似。 它构造一个 JSON 数组,每传递一个参数都会将相应的值推送到数组中。

SELECT JSON_ARRAY(Type, Description) AS pastryJSON FROM Baking.Pastry


Row  pastryJSON

---- ---------------

1    ["Choux Pastry" , "A light pastry that is often filled with cream"]

2    ["Puff Pastry" , "Many layers that cause the pastry to expand or puff when baked"]

3    ["Filo Pastry" , "Multiple layers of a paper-thin dough wrapped around a filling"]

JSON_ARRAY 是一个非常简单的函数。 此示例创建一个数组,其中每行包含两个元素。 第一项由列 Type 的值填充,而第二项由列 Description 的值填充。

高级方案

也许你需要创建一个更复杂的 JSON 结构。 值参数可以是嵌套的 JSON_ARRAYJSON_OBECT 函数调用,从而可以构造嵌套的 JSON 结构。 我们回到第一个示例,将 JSON 对象包装在标头结构中:

SELECT JSON_OBJECT('food_type' : 'pastry', 'details' : JSON_OBJECT('type' : Type, 'description' : Description)) AS pastryJSON FROM Baking.Pastry


Row  pastryJSON

---- ---------------

1    {"food_type" : "pastry", "details" : {"type" : "Choux Pastry", "description" : "A light pastry that is often filled with cream"}}

2    {"food_type" : "pastry", "details" : {"type" : "Puff Pastry", "description" : " Many layers that cause the pastry to expand or puff when baked"}}

3    {"food_type" : "pastry", "details" : {"type" : "Filo Pastry", "description" : " Multiple layers of a paper-thin dough wrapped around a filling"}}

我们计划在将来的版本中实现更多的 JSON SQL 函数,但这两个函数是坚实的开始。 主要用例是从关系型数据构造简单的 JSON 元素,而无需编写代码。 这样即使无法更改后端代码,也可以从系统直接发布 JSON。

要创建更复杂的结构,使用新的复合接口可以更高效地构建,该接口允许将Persistent/Registered对象转换为动态对象/数组。 然后可以根据需要轻松修改结构,最后通过 $toJSON() 调用将其导出为 JSON 字符串。 我会在将来的帖子中更详细地介绍这个主题。

0
3 950