Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incorrect array deserialisation in ServiceStack.Text

I have this JSON:-

{"snippet-format":"raw","total":1,"start":1,"page-length":200,"results":[{"index":1,"uri":"/myproject/info.xml","path":"fn:doc(\"/myproject/info.xml\")","score":0,"confidence":0,"fitness":0,"content":"<root xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"\" xmlns:search=\"http://marklogic.com/appservices/search\"><content>Adams Project file</content></root>"}],"facets":{"lastmodified":{"type":"xs:dateTime","facetValues":[]}},"metrics":{"query-resolution-time":"PT0.002559S","facet-resolution-time":"PT0.00111S","snippet-resolution-time":"PT0.000043S","total-time":"PT0.0039S"}}

Which I'm deserialising using this object :-

public class SearchResponse : MarkLogicObject {
  // response fields
  public string SnippetFormat {get;set;}
  public int Total {get;set;}
  public int Start {get;set;}
  public int PageLength {get;set;}
  public SearchResult[] Results { get; set; }
  public string Warning {get;set;}

  public override string ToString ()
  {
    return "SnippetFormat: " + SnippetFormat + ", Total: " + Total + ", Start: " + Start + ", Warning: " + Warning;
  }

  public static SearchResponse ParseJson(string json) {
    var map = JsonObject.Parse(json);

    return new SearchResponse {
      Total = int.Parse (map["total"]),
      Start = int.Parse (map["start"]),
      PageLength = int.Parse (map ["page-length"]),
      SnippetFormat = map ["snippet-format"],
      Warning = map["warning"],
      Results = map["results"].FromJson<SearchResult[]>() // why doesn't this deserialise properly? It creates a SearchResponse object mirroring this one instead.
    };
  }
}

// Sub elements of SearchResponse

public class SearchResult
{
  public string Uri {get;set;}
  public long Index { get; set; }
  public string Path {get;set;}
  public double Score {get;set;}
  public double Fitness {get;set;}
  public double Confidence {get;set;}
  public string Content { get; set; } // JSON or XML content (probably XML, no matter what ?format=json says)

  public override string ToString ()
  {
    return string.Format ("[SearchResult: Uri={0}, Index={1}, Path={2}, Score={3}, Fitness={4}, Confidence={5}, Content={6}]", Uri, Index, Path, Score, Fitness, Confidence, Content);
  }
}

The SearchResponse itself is interpreted fine (yay!) but the Search Result child object is incorrect - it looks just like the top level SearchResponse.

Here's what the following log lines give me:-

HttpWebResponse webResponse = restClient.Get<HttpWebResponse>(completePath("/v1/search",qp));

  using (var stream = webResponse.GetResponseStream())
  using (var sr = new StreamReader(stream)) {
    var text = sr.ReadToEnd();
    log.log ("response text: " + text);
    result = text.FromJson<SearchResponse>();
  }

  log.log ("RESULT: " + result.ToString ());
  for (int i = 0; i < result.Results.Length; i++) {
    log.log ("Result " + i + ": " + result.Results[i].ToString());
  }

This log output:-

19:30:24 | SparkleMLLogger | RESULT: SnippetFormat: raw, Total: 1, Start: 1, Warning: 
19:30:24 | SparkleMLLogger | Result: SnippetFormat: raw, Total: 1, Start: 1, Warning: 

Anyone have an idea how to fix it?

Thanks in advance.

like image 227
adamfowleruk Avatar asked Oct 05 '22 15:10

adamfowleruk


1 Answers

If you're choosing to dynamically parse JSON, the values that are retrieved from the JsonObject are by default escaped scalar values. If you want to parse an array you need to use the T.ArrayObjects() extension method, e.g:

JsonArrayObjects results = JsonObject.Parse(json).ArrayObjects("results");
string url = results[0]["uri"];
url.Print(); // /myproject/info.xml

If you want to get the raw un-escaped value from the JsonObject you need retrieve it via the GetUnescaped() method, e.g:

SearchResult[] typedResults = JsonObject.Parse(json)
    .GetUnescaped("results")
    .FromJson<SearchResult[]>();

typedResults.PrintDump();

Output:

[
    {
        Uri: /myproject/info.xml,
        Index: 1,
        Path: "fn:doc(""/myproject/info.xml"")",
        Score: 0,
        Fitness: 0,
        Confidence: 0,
        Content: "<root xmlns:xs=""http://www.w3.org/2001/XMLSchema"" xmlns="""" xmlns:search=""http://marklogic.com/appservices/search""><content>Adams Project file</content></root>"
    }
]
like image 191
mythz Avatar answered Oct 13 '22 09:10

mythz