Sistemas de Códigos
FHIR usa valores codificados extensivamente para representar categorías, estados, tipos y otros conceptos enumerados. La biblioteca gofhir/models genera tipos enum basados en cadenas con seguridad de tipos para cada sistema de códigos FHIR, proporcionando seguridad en tiempo de compilación y autocompletado del IDE para campos codificados.
Patrón de Enum Generado
Cada sistema de códigos FHIR se representa como un alias de tipo de string en Go, con constantes para cada valor de código válido. Todos los tipos enum y constantes se generan en codesystems.go a partir de las definiciones ValueSet de FHIR.
La convención de nomenclatura sigue este patrón:
- Nombre del tipo: El nombre del sistema de códigos FHIR en PascalCase (p. ej.,
AdministrativeGender) - Nombre de la constante: El nombre del tipo seguido del valor del código en PascalCase (p. ej.,
AdministrativeGenderMale) - Valor de la constante: La cadena del código FHIR (p. ej.,
"male")
AdministrativeGender
Uno de los sistemas de códigos más utilizados:
type AdministrativeGender string
const (
AdministrativeGenderMale AdministrativeGender = "male"
AdministrativeGenderFemale AdministrativeGender = "female"
AdministrativeGenderOther AdministrativeGender = "other"
AdministrativeGenderUnknown AdministrativeGender = "unknown"
)Usado en el struct Patient:
type Patient struct {
// ...
Gender *AdministrativeGender `json:"gender,omitempty"`
GenderExt *Element `json:"_gender,omitempty"`
// ...
}Ejemplo de uso:
func ptrTo[T any](v T) *T {
return &v
}
patient := &r4.Patient{
Id: ptrTo("example"),
Gender: ptrTo(r4.AdministrativeGenderFemale),
}
// Read the value
if patient.Gender != nil {
switch *patient.Gender {
case r4.AdministrativeGenderMale:
fmt.Println("Male")
case r4.AdministrativeGenderFemale:
fmt.Println("Female")
case r4.AdministrativeGenderOther:
fmt.Println("Other")
case r4.AdministrativeGenderUnknown:
fmt.Println("Unknown")
}
}ObservationStatus
Un ejemplo completo mostrando todos los valores del sistema de códigos ObservationStatus:
type ObservationStatus string
const (
ObservationStatusRegistered ObservationStatus = "registered"
ObservationStatusPreliminary ObservationStatus = "preliminary"
ObservationStatusFinal ObservationStatus = "final"
ObservationStatusAmended ObservationStatus = "amended"
ObservationStatusCorrected ObservationStatus = "corrected"
ObservationStatusCancelled ObservationStatus = "cancelled"
ObservationStatusEnteredInError ObservationStatus = "entered-in-error"
ObservationStatusUnknown ObservationStatus = "unknown"
)Uso en un recurso Observation:
package main
import (
"fmt"
"github.com/gofhir/models/r4/v2"
)
func ptrTo[T any](v T) *T {
return &v
}
func main() {
obs := &r4.Observation{
Id: ptrTo("vitals-1"),
Status: ptrTo(r4.ObservationStatusFinal),
Code: &r4.CodeableConcept{
Coding: []r4.Coding{
{
System: ptrTo("http://loinc.org"),
Code: ptrTo("8867-4"),
Display: ptrTo("Heart rate"),
},
},
},
ValueQuantity: &r4.Quantity{
Value: r4.MustDecimal("72"),
Unit: ptrTo("beats/minute"),
System: ptrTo("http://unitsofmeasure.org"),
Code: ptrTo("/min"),
},
}
fmt.Printf("Observation %s: %s\n", *obs.Id, *obs.Status)
// Output: Observation vitals-1: final
}Otros Sistemas de Códigos Comunes
La biblioteca genera tipos enum para todos los sistemas de códigos FHIR. Aquí tienes algunos de los más utilizados:
BundleType
type BundleType string
const (
BundleTypeDocument BundleType = "document"
BundleTypeMessage BundleType = "message"
BundleTypeTransaction BundleType = "transaction"
BundleTypeBatch BundleType = "batch"
BundleTypeSearchset BundleType = "searchset"
// ...
)AccountStatus
type AccountStatus string
const (
AccountStatusActive AccountStatus = "active"
AccountStatusInactive AccountStatus = "inactive"
AccountStatusEnteredInError AccountStatus = "entered-in-error"
AccountStatusOnHold AccountStatus = "on-hold"
AccountStatusUnknown AccountStatus = "unknown"
)NameUse
type NameUse string
const (
NameUseUsual NameUse = "usual"
NameUseOfficial NameUse = "official"
NameUseTemp NameUse = "temp"
NameUseNickname NameUse = "nickname"
NameUseAnonymous NameUse = "anonymous"
NameUseOld NameUse = "old"
NameUseMaiden NameUse = "maiden"
)ContactPointSystem
type ContactPointSystem string
const (
ContactPointSystemPhone ContactPointSystem = "phone"
ContactPointSystemFax ContactPointSystem = "fax"
ContactPointSystemEmail ContactPointSystem = "email"
ContactPointSystemPager ContactPointSystem = "pager"
ContactPointSystemUrl ContactPointSystem = "url"
ContactPointSystemSms ContactPointSystem = "sms"
ContactPointSystemOther ContactPointSystem = "other"
)Beneficios de la Seguridad de Tipos
Usar tipos enum generados en lugar de cadenas sin procesar proporciona varias ventajas:
Validación en Tiempo de Compilación
El compilador detecta errores tipográficos y valores inválidos:
// This compiles -- valid code
patient.Gender = ptrTo(r4.AdministrativeGenderMale)
// This would cause a compile error if you tried to assign an invalid string
// patient.Gender = ptrTo("mael") // type mismatchAutocompletado del IDE
Cuando escribes r4.AdministrativeGender, tu IDE sugerirá todos los valores válidos, facilitando descubrir los códigos disponibles sin consultar la especificación FHIR.
Código Auto-documentado
Las constantes de los enums llevan el nombre de visualización del ValueSet de FHIR como comentario, y el mismo texto está disponible en tiempo de ejecución — ver Lo que cada código sabe de sí mismo más abajo.
// AdministrativeGenderMale - Male
AdministrativeGenderMale AdministrativeGender = "male"
// AdministrativeGenderFemale - Female
AdministrativeGenderFemale AdministrativeGender = "female"Lo que cada código sabe de sí mismo
Un código por sí solo no es un coding: "male" no dice nada hasta que dices de qué
vocabulario viene. Todo enum generado lleva los datos que la especificación da para
cada uno de sus códigos:
g := r4.AdministrativeGenderMale
g.Display() // "Male"
g.System() // "http://hl7.org/fhir/administrative-gender"
g.Coding() // Coding{System: ..., Code: "male", Display: "Male"}Coding() es lo que necesita un CodeableConcept, y construirlo a mano es donde
la URL del system se suele copiar mal:
cc := r4.NewCodeableConceptBuilder().
AddCoding(r4.AdministrativeGenderMale.Coding()).
Build()
// {"coding":[{"system":"http://hl7.org/fhir/administrative-gender","code":"male","display":"Male"}]}Listar los valores
<Tipo>Values() devuelve todos los códigos que el tipo admite, en el orden de la
especificación — para poblar un formulario o comprobar que un switch los cubre
todos:
for _, g := range r4.AdministrativeGenderValues() {
fmt.Printf("%s\t%s\n", g, g.Display())
}
// male Male
// female Female
// other Other
// unknown UnknownUn código que no está en el ValueSet
El tipo es un string, así que se le puede asignar cualquier cosa, incluido un valor que llegó por la red y nadie comprobó. Deserializar no lo rechaza:
json.Unmarshal([]byte(`{"resourceType":"Patient","gender":"not-a-gender"}`), &p)
// sin errorDisplay() recurre al propio código en lugar de a una cadena vacía, así que el
resultado sigue pudiendo mostrarse a una persona, y System() devuelve "" en
lugar de afirmar un vocabulario que no se sostiene.
Para saber si un código es uno de los que define la especificación, consulta la tabla directamente:
if _, definido := r4.AdministrativeGenderTable[*p.Gender]; !definido {
// el código está fuera del ValueSet
}required —un binding
extensible o preferred deja el campo como *string, precisamente porque allí
un código de fuera del ValueSet es válido—, pero la conformidad es una pregunta
para gofhir/validator, que trabaja sobre
el documento crudo. Una comprobación de pertenencia aquí se parecería demasiado a
una validación terminológica como para no confundirse con ella.Trabajo con Valores de Cadena
Dado que los tipos enum se basan en string, puedes convertir entre ellos y cadenas sin procesar cuando sea necesario:
// From enum to string
gender := r4.AdministrativeGenderMale
s := string(gender) // "male"
// From string to enum
input := "female"
gender = r4.AdministrativeGender(input)Esto es útil al leer valores de código desde fuentes externas como archivos de configuración o bases de datos.
Serialización JSON
Los tipos enum se serializan hacia y desde sus valores de cadena en JSON, exactamente como especifica FHIR:
patient := &r4.Patient{
Gender: ptrTo(r4.AdministrativeGenderMale),
}
data, _ := json.Marshal(patient)
// {"resourceType":"Patient","gender":"male"}
var decoded r4.Patient
json.Unmarshal(data, &decoded)
fmt.Println(*decoded.Gender) // "male"
fmt.Println(*decoded.Gender == r4.AdministrativeGenderMale) // trueSerialización XML
En XML, los valores de código se codifican usando la codificación estándar de primitivos FHIR con un atributo value. La biblioteca maneja esto a través del helper genérico xmlEncodePrimitiveCode:
<Patient xmlns="http://hl7.org/fhir">
<gender value="male"/>
</Patient>*string para permitir cualquier valor de código, ya que esas vinculaciones permiten códigos adicionales más allá de los definidos en el conjunto de valores.