tags:

views:

1331

answers:

2

I'm trying to write an OpenSearch Suggestion service that complies with the OpenSearch spec.

http://www.opensearch.org/Specifications/OpenSearch/Extensions/Suggestions

This spec requires the service to return a JSON array with the first element being a string and the following elements being arrays of strings. I'm able to get it almost there by returning an array of strings (string[][]) and having WCF serialize this into JSON. However, in order to comply with the spec, I tried to return an array of objects (object[]), with the first one being a string, and the rest being arrays of strings (string[]).

Whenever I try to return the array of objects, it doesn't work, such as this:

From service:

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class SuggestionService : ISuggestionService
{
    public object[] Search(string searchTerms)
    {
        SearchSuggestions = new object[4];
        SearchText = searchTerms;
        SearchSuggestions[0] = SearchText;
        Text = new string[10];
        Urls = new string[10];
        Descriptions = new string[10];

        // Removed irrelevant ADO.NET code
        while (searchResultReader.Read() && index < 10)
        {

            Text[index] = searchResultReader["Company"].ToString();
            Descriptions[index] = searchResultReader["Company"].ToString();
            Urls[index] = "http://dev.localhost/Customers/EditCustomer.aspx?id=" +
                          searchResultReader["idCustomer"];
            index++;
        }

        SearchSuggestions[1] = Text;
        SearchSuggestions[2] = Descriptions;
        SearchSuggestions[3] = Urls;
        return SearchSuggestions;
    }

    [DataMember]
    public string SearchText { get; set; }

    [DataMember]
    public string[] Text { get; set; }

    [DataMember]
    public string[] Descriptions { get; set; }

    [DataMember]
    public string[] Urls { get; set; }

    [DataMember]
    public object[] SearchSuggestions { get; set; }
}

Here's the entire interface:

[ServiceContract]
public interface ISuggestionService
{
    [OperationContract]
    [WebGet(UriTemplate = "/Search?q={searchTerms}",
        BodyStyle = WebMessageBodyStyle.Bare,
        ResponseFormat = WebMessageFormat.Json)]
    object[] Search(string searchTerms);
}

This causes the service to return "Error 324 (net::ERR_EMPTY_RESPONSE): Unknown error." This is the only error I've been able to get.

Am I not able to use an array of objects to store one string and three arrays? What else could I do in order to use WCF to return the proper JSON that complies with this spec?

EDIT: Added lots more of the code

+3  A: 

You posted a bunch of [DataMember]'s. Please post the entire [DataContract]. Also show us the JSON returned when you return that DataContract.


A Data Contract should never include behavior. Try the following (I haven't had a chance to test it, and will need to fake up the data to do so):

[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class SuggestionService : ISuggestionService
{
    public SearchResults Search(string searchTerms)
    {
        var results = new SearchResults
                          {
                              SearchText = searchTerms,
                              Text = new string[10],
                              Urls = new string[10],
                              Descriptions = new string[10]
                          };

        // Removed irrelevant ADO.NET code
        int index = 0;
        while (searchResultReader.Read() && index < 10)
        {

            results.Text[index] = searchResultReader["Company"].ToString();
            results.Descriptions[index] = searchResultReader["Company"].ToString();
            results.Urls[index] = "http://dev.localhost/Customers/EditCustomer.aspx?id=" +
                          searchResultReader["idCustomer"];
            index++;
        }

        return results;
    }
}

[DataContract]
public class SearchResults
{
    [DataMember]
    public string SearchText { get; set; }

    [DataMember]
    public string[] Text { get; set; }

    [DataMember]
    public string[] Descriptions { get; set; }

    [DataMember]
    public string[] Urls { get; set; }
}


Ok, I read enough of that spec and of the JSON spec, to convince myself you really do need to return an array of objects, and not an instance of a class that contains an array of objects. Of course, that didn't quite work. Here's what you needed:

[ServiceContract]
[ServiceKnownType(typeof(string))]
[ServiceKnownType(typeof(string[]))]
public interface ISuggestionService
{
    [OperationContract]
    [WebGet(UriTemplate = "/Search?q={searchTerms}",
        BodyStyle = WebMessageBodyStyle.Bare,
        ResponseFormat = WebMessageFormat.Json)]
    object[] Search(string searchTerms);
}

I just tried it, and it worked. Here's the JSON (indentation added):

[
    "abc",
    ["Company1","Company2","Company3",...],
    ["Company1 Description","Company2 Description","Company3 Description",...],
    ["http:\/\/dev.localhost\/Customers\/EditCustomer.aspx?id=1",
     "http:\/\/dev.localhost\/Customers\/EditCustomer.aspx?id=2",
     "http:\/\/dev.localhost\/Customers\/EditCustomer.aspx?id=3",...]
]
John Saunders
Thanks for responding, I added all of the relevant code.
Chris Jackson
This is what I originally tried, the problem is that it returns the names of the objects in the JSON, which is outside the spec. It needs to return something like this (From reference):["sea",["sears","search engines","search engine","search","sears.com","seattle times"],["7,390,000 results","17,900,000 results","25,700,000 results","1,220,000,000 results","1 result","17,600,000 results"],["http://example.com?q=sears","http://example.com?q=search+engines","http://example.com?q=search+engine","http://example.com?q=search","http://example.com?q=sears.com","http://example.com?q=seattle+times"]]
Chris Jackson
I can return an jagged array of strings (string[][]) and it's almost there, the problem is that the first element of the JSON array is surround by brackets, which is outside of the spec, which is why I was trying to return an array of objects (object[]).
Chris Jackson
That spec was fairly large. Can you post a URL to the specific part of it, or at least tell me what to search for?
John Saunders
You are the man! I can't tell you how much time I spent trying to figure this out. Works beautifully.
Chris Jackson
Glad it works. And thank _you_ for forcing me to create my first JSON WCF Service.
John Saunders
+2  A: 

This is a problem that had me stumped for a while as well - there's a complete end-to-end walkthrough of how to do this, including how to support both JSON and XML opensearch (including XML attribute serialization), with downloadable code, at "Building Labs – Writing an OpenSearch Suggestions provider in C# with WCF".

Andras Zoltan