Transforming a json escaped string #1131
-
I've got this payload which contains a {
"Data": {
"req": {
"Url": "http://localhost:7071/api/order",
"Method": "POST",
"Query": {},
"Headers": {
"Accept" : ["application/json" ],
"Connection" : ["keep-alive" ],
"Host" : ["localhost:7071" ],
"User-Agent" : ["PostmanRuntime/7.43.0" ],
"Accept-Encoding": ["gzip, deflate, br" ],
"Content-Type" : ["application/json" ],
"Content-Length" : ["45" ],
"Postman-Token" : ["1107e3bb-3015-417e-ad97-082ce92c5b14"]
},
"Params": {},
"Body": "{\n \"productId\": \"abc\",\n \"quantity\": 3\n}"
}
}
} I'd need to unescape / transform / parse Body so that it ends up like: {
"Data": {
"req": {
"Url": "http://localhost:7071/api/order",
"Method": "POST",
"Query": {},
"Headers": {
"Accept" : ["application/json" ],
"Connection" : ["keep-alive" ],
"Host" : ["localhost:7071" ],
"User-Agent" : ["PostmanRuntime/7.43.0" ],
"Accept-Encoding": ["gzip, deflate, br" ],
"Content-Type" : ["application/json" ],
"Content-Length" : ["45" ],
"Postman-Token" : ["1107e3bb-3015-417e-ad97-082ce92c5b14"]
},
"Params": {},
"Body": {"productId": "abc", "quantity": 3}
}
}
} I've tried a few ways such as: export const orderItemSchema = t.Object({
productId: t.String(),
quantity: t.Number(),
})
const ProductOrder = t.Transform(t.String())
.Decode(value => {
const json = JSON.parse(value)
return Value.Parse(orderItemSchema, json)
})
.Encode(value => JSON.stringify(value))
export const httpInputBindingSchema = t.Object({
Url: t.String(),
Method: t.Union([t.Literal('GET'), t.Literal('POST')]),
Query: t.Record(t.String(), t.String()),
Headers: t.Record(t.String(), t.Array(t.String())),
Params: t.Record(t.String(), t.String()),
Body: ProductOrder,
}) but Body always ends up as a string in the resulting payload. Any ideas? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
@nad-au Hi, The following seems to work ok (validating the import { Type as t } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'
export const orderItemSchema = t.Object({
productId: t.String(),
quantity: t.Number(),
})
const ProductOrder = t.Transform(t.String())
.Decode(value => Value.Parse(orderItemSchema, JSON.parse(value)))
.Encode(value => JSON.stringify(value))
export const httpInputBindingSchema = t.Object({
Url: t.String(),
Method: t.Union([t.Literal('GET'), t.Literal('POST')]),
Query: t.Record(t.String(), t.String()),
Headers: t.Record(t.String(), t.Array(t.String())),
Params: t.Record(t.String(), t.String()),
Body: ProductOrder,
})
const result = Value.Parse(httpInputBindingSchema, {
"Url": "http://localhost:7071/api/order",
"Method": "POST",
"Query": {},
"Headers": {
"Accept": ["application/json"],
"Connection": ["keep-alive"],
"Host": ["localhost:7071"],
"User-Agent": ["PostmanRuntime/7.43.0"],
"Accept-Encoding": ["gzip, deflate, br"],
"Content-Type": ["application/json"],
"Content-Length": ["45"],
"Postman-Token": ["1107e3bb-3015-417e-ad97-082ce92c5b14"]
},
"Params": {},
"Body": "{\n \"productId\": \"abc\",\n \"quantity\": 3\n}"
})
console.log(result) // { ..., Body: { productId: 'abc', quantity: 3 } } Your implementation of Transform is correct, and the above does seem to decode the Body ok. If you are seeing issues with the above and you are using a framework to receive the request, just be mindful that Transforms will only work if you are validating with TypeBox validation (either Parse or Decode). If you are using Ajv to validate (as many frameworks do), the Transform will be skipped and you will only see the string value. If this is the case, you may need to decode the Body manually inside your route handler. Hope this helps |
Beta Was this translation helpful? Give feedback.
-
When using export const validateAndParseSchema = <Type extends TSchema, Output = StaticDecode<Type>, TResult extends Output = Output>(schema: TSchema, body: unknown): Result<TResult, ValueError[]> => {
try {
return Ok(Value.Parse(schema, body));
} catch (e) {
if (e instanceof AssertError) {
const iterator = e.Errors()
const errors = [...iterator]
return Err(errors)
}
else if (e instanceof TransformDecodeError) {
const innerError = e.error
if (innerError instanceof AssertError) {
const iterator = innerError.Errors()
const errors = [...iterator]
return Err(errors)
}
return Err([{
type: ValueErrorType.Object,
schema: e.schema,
path: e.path,
value: e.value,
message: e.message,
errors: []
}])
}
else if (e instanceof TypeBoxError) {
throw new Error(`Unknown TypeBoxError: ${e.message}`, { cause: e })
}
throw e
}
} |
Beta Was this translation helpful? Give feedback.
@nad-au Hi,
The following seems to work ok (validating the
Data.req
part of the payload only)