Error with set parObj = {}.%FromJSON(%request.Content.ReadLineIntoStream())

I try to test our REST/JSON (POST) services,where queryparametres are in content as json and the answer in contet is also as json.

This is my testClient:

Class XXX.RESTClient Extends %RegisteredObject
{
ClassMethod TestXXX()
{
#dim tRequest As %Net.HttpRequest = ##class(%Net.HttpRequest).%New()

set tRequest.ContentType="application/json"
json=##class(%ZEN.proxyObject).%New()
json.x="XXX" 
##class(%ZEN.Auxiliary.jsonProvider).%WriteJSONStreamFromObject(.jsonStream,json)
set jsonText=jsonStream.Read(jsonStream.Size)
set tSC=tRequest.EntityBody.Write(jsonText)

set tSC = tRequest.Post("http://server:57776/csp/XXX/testXXX")

do tRequest.HttpResponse.OutputToDevice()
}

}

in dispatchClass:

ClassMethod testXXX() As %Status
{
#dim tStatus As %Status = $$$OK
#dim parObj As %DynamicAbstractObject = {}
#dim dynObj As %DynamicAbstractObject = {}
set $ZT="ERROR"
goto:'..%securityCheck(.tStatus,parObj) DONE

DONE ;
do ..%logout(.tStatus,dynObj)
quit tStatus
ERROR ;
set tStatus = $$$ERROR($$$CacheError,$ZE)
do BACK^%ETN
goto DONE
}
 

ClassMethod %securityCheck(ByRef pStatus As %Status, parObj As %DynamicAbstractObject) As %Boolean
{
set pStatus = $$$OK
set $ZT="ERROR"

#dim %request As %CSP.Request

if %request.ContentType'=..#CONTENTTYPE {
set pStatus=$$$ERROR($$$GeneralError,"Wrong ContentType")
goto DONE
}
if '$IsObject(%request.Content) {
set pStatus=$$$ERROR($$$GeneralError,"No Content")
goto DONE
}

/*
//THIS WRITES OK { "x":"XXX"}

do %request.Content.Rewind()
Set fstream=##class(%FileBinaryStream).%New()
do fstream.LinkToFile("content.txt")
do %request.Content.Rewind()
do fstream.CopyFrom(%request.Content)
do fstream.%Save()
AND THIS IS OK (the same):

do %request.Content.OutputToDevice()

*/
//Content is type of %CSP.BinaryStream
do %request.Content.Rewind()
set parObj = {}.%FromJSON(%request.Content.ReadLineIntoStream()) //<== THERE I GET THE ERROR
do %request.Content.Rewind()

***

goto DONE 
}

DONE ;
quit $$$ISOK(pStatus)
ERROR ;
set pStatus = $$$ERROR($$$CacheError,$ZE)
do BACK^%ETN
goto DONE
}

THIS IS THE ERROR

{"status":{"koodi":"0","selite":"Cache error: <THROW>%FromJSON+37^%Library.DynamicAbstractObject.1 *%Exception.General Premature end of data 12 Line 1 Offset 1 "}}{
        "errors":[ {
                        "code":5002,
                        "domain":"%ObjectErrors",
                        "error":"ERROR #5002: Cache error: <THROW>%FromJSON+37^%Library.DynamicAbstractObject.1 *%Exception.General Premature end of data 12 Line 1 Offset 1 ",
                        "id":"CacheError",
                        "params":["<THROW>%FromJSON+37^%Library.DynamicAbstractObject.1 *%Exception.General Premature end of data 12 Line 1 Offset 1 "
                        ]
                }
        ],
        "summary":"ERROR #5002: Cache error: <THROW>%FromJSON+37^%Library.DynamicAbstractObject.1 *%Exception.General Premature end of data 12 Line 1 Offset 1 "
}

WRC told Content is type on %CSP.Stream, not binaryStream. True, if you read %CSP.request, but this works in another application. And in errorlog Content is told to be binary stream.

+----------------- swizzled references ---------------
|          i%Content = "3@%CSP.BinaryStream"
|          r%Content = ""
+-----------------------------------------------------
 

What is my error? How should I change this? Some changes to testClient, not service itself?

  • 0
  • 0
  • 432
  • 3
  • 2

Answers

you do .ReadLineIntoStream()  
doc says: https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls

This reads from the stream until it find the LineTerminator and returns this as a stream. If the stream does not contain the line terminator this can potentially be the entire stream.

and your error message:  Premature end of data 12 Line 1 Offset 1     

indicates that you have hit some character interpreted as line terminator after 12 characters. (rather short for  JSON)
My guess: your JSON input is a multiline input with enough line terminators inside

As a consequence your JSON input is incomplete.

Suggested workaround

  • get length of your stream method SizeGet()
  • read full stream ignoring line terminators using method Read(ByRef len As %Integer, ByRef sc As %Status) as %CacheString

eventually, it might be necessary to remove the line terminators before  %FromJSON

OK.  for some reason the most important part of the link was truncated.

https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25CSP.BinaryStream

I hope it doesn't hide again.

The basic mistake happens here the definition of Request 
https://docs.intersystems.com/latest/csp/documatic/%25CSP.Documatic.cls?PAGE=CLASS&LIBRARY=%25SYS&CLASSNAME=%25CSP.Request

And you are right. %CSP.Stream has no Read method because  ContentType tells you the true object . 
As in the example:

It could have been %CSP.CharacterStream as well.

Both extend over some steps %GlobalStreamAdaptor which have all the READ, WRITE, ....methods

Just reading docs and not checking inherited methods (e.g. in Studio) is mostly misleading.

Aha, that is how "ContentType" take effect. Thanks, Robert!

I would like to suggest the doc people to make  it more  clear though.

You can just pass the stream directly, no need to read from it:

set parObj = {}.%FromJSON(%request.Content)