forked from eug48/fhir
/
position.go
162 lines (140 loc) · 4.39 KB
/
position.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package models2
import (
"fmt"
"strings"
"github.com/buger/jsonparser"
)
type FhirSchemaError struct {
msg string
at string
}
func (e FhirSchemaError) Error() string {
return fmt.Sprintf("FHIR schema error at %s: %s", e.at, e.msg)
}
type positionInfo struct {
// FHIR 'element' that we're currently parsing - found in fhir_types.go
element string
// For contained & included resources we need to peek into each JSON array element to get the resource type
needToReadResourceType bool
// Current path through the JSON - only for debugging
pathHere string
}
func (p *positionInfo) atReference() bool {
return p.element == "Reference"
}
func (p *positionInfo) atExtension() bool {
return p.element == "Extension"
}
func (p *positionInfo) atDecimal() bool {
return p.element == "decimal"
}
func (p *positionInfo) atDate() bool {
return p.element == "date" || p.element == "dateTime"
}
func (p *positionInfo) atInstant() bool {
return p.element == "instant"
}
func (p *positionInfo) downTo(key string, valueJson []byte) positionInfo {
result := p.__downTo(key, valueJson)
debug("downTo %s --> %#v", key, result)
return result
}
func (p *positionInfo) __downTo(key string, valueJson []byte) positionInfo {
if p.needToReadResourceType {
panic(fmt.Errorf("error parsing JSON: need to acquire resource type"))
}
var needToAcquireResourceType bool
nextPath := p.pathHere + "." + key
// resourceType key isn't included in our schema extract
if key == "resourceType" && !strings.Contains(p.element, ".") {
return positionInfo{
pathHere: nextPath,
element: "string",
}
}
if strings.HasPrefix(key, "_") {
// primitive extension
return positionInfo{
pathHere: nextPath,
element: "_",
}
}
nextElement := p.element + "." + key
t, found := fhirTypes[nextElement]
if !found {
panic(p.schemaError("failed to get type for %s (at %s --> %s)", nextElement, p.element, key))
}
if t != "BackboneElement" && t != "Element" {
// For BackboneElement and Element we continue adding to the current 'path'
// e.g.
// if we're at "DataRequirement.codeFilter": "Element",
// we don't want to move to element but at the next field we can move downTo
// "DataRequirement.codeFilter.valueCodeableConcept": "CodeableConcept",
nextElement = t
}
if nextElement == "Resource" {
_, dataType, _, err := jsonparser.Get(valueJson)
if err != nil {
panic(p.schemaError("failed to get json type of nested resource (%s)", key))
}
if dataType == jsonparser.Array {
needToAcquireResourceType = true // not in array element yet
} else if dataType == jsonparser.Object {
resourceType, err := jsonparser.GetString(valueJson, "resourceType")
if err != nil {
panic(p.schemaError("failed to get resourceType of nested resource (%s)", key))
}
nextElement = resourceType
nextPath = nextPath + "(" + nextElement + ")"
_, found = fhirTypes[nextElement+".id"]
if !found {
panic(p.schemaError("failed to get type for contained resource %s (%s)", nextPath, key))
}
} else {
panic(p.schemaError("failed to get JSON of nested resource (%s): neither object nor array (%d)", key, dataType))
}
}
return positionInfo{
pathHere: nextPath,
element: nextElement,
needToReadResourceType: needToAcquireResourceType,
}
}
func (p *positionInfo) intoArray(valueJson []byte) positionInfo {
result := p.__intoArray(valueJson)
debug("intoArray --> %#v", result)
return result
}
func (p *positionInfo) __intoArray(valueJson []byte) positionInfo {
nextPath := p.pathHere + ".[]"
nextElement := p.element
if p.needToReadResourceType {
p.needToReadResourceType = false
resourceType, err := jsonparser.GetString(valueJson, "resourceType")
if err != nil {
panic(p.schemaError("failed to get resourceType of array resource: %+v", err))
}
var found bool
nextElement = resourceType
nextPath = nextPath + "(" + nextElement + ")"
_, found = fhirTypes[nextElement+".id"]
if !found {
panic(p.schemaError("failed to get type for contained resource at array (%s)", resourceType))
}
}
return positionInfo{
pathHere: nextPath,
element: nextElement,
}
}
func (p *positionInfo) schemaError(format string, a ...interface{}) FhirSchemaError {
return FhirSchemaError{
at: p.pathHere,
msg: fmt.Sprintf(format, a...),
}
}
func init() {
// TODO: add to map
fhirTypes["_.id"] = "string"
fhirTypes["_.extension"] = "Extension"
}