You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何移除FHIR JSON输出中多余的value嵌套结构?

问题

开发.NET Core应用时,基于数据库结果创建FHIR资源,生成FHIR Bundle的JSON响应出现多余的value嵌套结构,需要移除这些层级,使输出符合标准FHIR格式。

当前JSON输出

{
  "id": {
    "value": "bundle-example"
  },
  "type": {
    "value": "searchset"
  },
  "total": {
    "value": 5
  },
  "entry": [
    {
      "fullUrl": {
        "value": "http://example.org/fhir/Observation/8393939"
      },
      "resource": {
        "id": {
          "value": "8393939"
        },
        "status": {
          "value": "final"
        },
        "code": {
          "coding": [
            {
              "system": {
                "value": "http://loinc.org"
              },
              "code": {
                "value": "1988-5"
              },
              "display": {
                "value": "CRP"
              }
            }
          ]
        }
        // ... other fields
      }
    }
    // ... other entries
  ]
}

期望JSON结构

{
  "resourceType": "Bundle",
  "id": "bundle-example",
  "type": "searchset",
  "total": 5,
  "entry": [
    {
      "fullUrl": "http://example.org/fhir/Observation/8393939",
      "resource": {
        "resourceType": "Observation",
        "id": "8393939",
        "status": "final",
        "code": {
          "coding": [
            {
              "system": "http://loinc.org",
              "code": "1988-5",
              "display": "CRP"
            }
          ]
        }
        // ... other fields
      }
    }
    // ... other entries
  ]
}

相关代码

using Hl7.Fhir.Model;
using System;
using System.Data;
using System.Collections.Generic;
using FHIR.PoC.Data;

namespace FHIR.PoC.Service
{
    public class ObservationService
    {
        private readonly SpecimenRepository m_specimenRepository;

        public ObservationService(SpecimenRepository specimenRepository)
        {
            m_specimenRepository = specimenRepository;
        }

        public Bundle MapToObservations(DataTable dtDbResults, bool bIncludeSpecimen)
        {
            var bundle = new Bundle
            {
                Id = "bundle-example",
                Type = Bundle.BundleType.Searchset,
                Total = dtDbResults.Rows.Count,
                Entry = new List<Bundle.EntryComponent>()
            };

            foreach (DataRow drRow in dtDbResults.Rows)
            {
                var observation = new Observation
                {
                    Id = drRow["zissample_id"].ToString() ?? "null",
                    Status = drRow["ZISRESULTSTATUS_ID"].ToString() == "8000013" ? ObservationStatus.Final : ObservationStatus.Preliminary,
                    Code = new CodeableConcept
                    {
                        Coding = new List<Coding>
                        {
                            new Coding
                            {
                                System = "http://loinc.org",
                                Code = drRow["LOINC_CODE"] != DBNull.Value ? drRow["LOINC_CODE"].ToString() : null,
                                Display = drRow["MEASURE_CODE"] != DBNull.Value ? drRow["MEASURE_CODE"].ToString() : null
                            }
                        }
                    },
                    Subject = new ResourceReference($"Patient/{drRow["SOURCE_ID"].ToString()}"),
                    Effective = GetEffectiveDate(drRow),
                    Issued = drRow["FIRSTREPORTDATETIME"] != DBNull.Value ? (DateTimeOffset?)Convert.ToDateTime(drRow["FIRSTREPORTDATETIME"]) : null,
                    Performer = new List<ResourceReference>
                    {
                        new ResourceReference("Practitioner/VasteWaardeLabTrain")
                    },
                    Value = GetObservationValue(drRow)
                };

                ProcessReferenceRangeIfPresent(drRow, observation);

                if (bIncludeSpecimen)
                {
                    ProcessSpecimen(drRow, observation);
                }

                bundle.Entry.Add(new Bundle.EntryComponent
                {
                    FullUrl = $"http://example.org/fhir/Observation/{observation.Id}",
                    Resource = observation
                });
            }

            return bundle;
        }
    }
}
解决方案

出现value嵌套是因为使用的FHIR.NET库默认采用原始元素序列化模式,而非标准FHIR JSON格式。只需调整序列化配置,就能输出符合要求的结构:

1. 使用FHIR专用序列化器

不要用.NET默认的System.Text.Json或Newtonsoft.Json直接序列化FHIR对象,必须使用库自带的序列化工具:

using Hl7.Fhir.Serialization;

// 序列化Bundle为标准FHIR JSON
var serializer = new FhirJsonSerializer(new SerializerSettings
{
    Pretty = true, // 可选,格式化输出便于阅读
    UseXmlSerialization = false
});
string standardFhirJson = serializer.SerializeToString(bundle);

2. ASP.NET Core项目配置全局格式化器

如果是在API接口中返回FHIR资源,直接在Startup/Program.cs中注册FHIR格式化器,控制器返回Bundle对象时会自动按标准格式序列化:

using Hl7.Fhir.AspNetCore;

// Program.cs中添加
builder.Services.AddControllers()
    .AddFhirFormatters();

3. 确认代码逻辑无需调整

你当前构建Bundle和Observation的属性赋值方式是正确的(如bundle.Id = "bundle-example"),问题不在对象构建环节,只要用FHIR专用序列化器,这些属性会自动转换为标准键值对,而非{ "value": "xxx" }结构。

4. 自动补充resourceType字段

标准FHIR JSON要求的resourceType字段,FHIR库会自动为Bundle、Observation等资源类添加,无需手动赋值,正确序列化后会自动出现在输出中。


内容的提问来源于stack exchange,提问作者Salt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 20:40:53