This project has moved. For the latest updates, please go here.
A tuple is a data structure that contains a number of objects of different types. A tuple is handy for some programming interfaces that allow only 1 parameter while you need to pass multiple parameters of different types. Without tuple, you may have 2 workarounds:
  1. An array of object. But you will lost the type information.
  2. A custom container class to contain the objects. However, it is cumbersome to define a contain class every time.

In C#, tuples are represented by 8 predefined generic types. Tuples with more than 7 elements should be represented by octuple, and the last element must be a tuple type.

In TypeScript, tuple types represent JavaScript arrays with individually tracked element types.

In WebApiClientGen, the representation of tuples is in favor of the C#'s constructions, that is, each element is associated with a property name like "Item1", "Item2" and "Item3" etc. Such approach makes the C# codes and TypeScript codes generated look alike for tuples.

And because Tuple is decorated by SerializableAttribute, in the deserialized data the properties has prefix "m" like "mItem1", To make the serialization with .NET client codes and TypeScript client codes work with the exact matching between service data models of tuple and client data models,, you may append a line for DefaultContractResolver in function Register of class WebApiConfig in the App_Start folder:

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API configuration and services
            // Configure Web API to use only bearer token authentication.
            config.SuppressDefaultHostAuthentication();
            config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));

            // Web API routes
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );

            //http://forums.asp.net/t/1821729.aspx?JsonMediaTypeFormatter+does+not+work+with+Tuple+int+List+string+
            config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
            //this will support Tuple serialization in JSON
        }
    }

And this solution will make the serialized data have properties like "Item1" and "Item2" etc., keeping the consistency in both ends.

Examples

C# client API codes:

        /// <summary>
        /// 
        /// GET api/Tuple/Tuple7
        /// </summary>
        public async Task<System.Tuple<string, string, string, string, string, long, int>> GetTuple7Async()
        {
            var template = new System.UriTemplate("api/Tuple/Tuple7");
            var uriParameters = new System.Collections.Specialized.NameValueCollection();
            var requestUri = template.BindByName(this.baseUri, uriParameters);
            var responseMessage = await client.GetAsync(requestUri.ToString());
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = await responseMessage.Content.ReadAsStreamAsync())
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<System.Tuple<string, string, string, string, string, long, int>>(jsonReader);
            }
        }
        
        /// <summary>
        /// 
        /// GET api/Tuple/Tuple7
        /// </summary>
        public System.Tuple<string, string, string, string, string, long, int> GetTuple7()
        {
            var template = new System.UriTemplate("api/Tuple/Tuple7");
            var uriParameters = new System.Collections.Specialized.NameValueCollection();
            var requestUri = template.BindByName(this.baseUri, uriParameters);
            var responseMessage = this.client.GetAsync(requestUri.ToString()).Result;
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = responseMessage.Content.ReadAsStreamAsync().Result)
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<System.Tuple<string, string, string, string, string, long, int>>(jsonReader);
            }
        }
        
        /// <summary>
        /// 
        /// POST api/Tuple/Tuple7
        /// </summary>
        public async Task<string> PostTuple7Async(System.Tuple<string, string, string, string, string, long, int> tuple)
        {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create();
            requestSerializer.Serialize(requestWriter, tuple);
            var requestUri = new System.Uri(this.baseUri, "api/Tuple/Tuple7");
            var content = new StringContent(requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");
            var responseMessage = await client.PostAsync(requestUri.ToString(), content);
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = await responseMessage.Content.ReadAsStreamAsync())
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<string>(jsonReader);
            }
            }
        }
        
        /// <summary>
        /// 
        /// POST api/Tuple/Tuple7
        /// </summary>
        public string PostTuple7(System.Tuple<string, string, string, string, string, long, int> tuple)
        {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create();
            requestSerializer.Serialize(requestWriter, tuple);
            var requestUri = new System.Uri(this.baseUri, "api/Tuple/Tuple7");
            var content = new StringContent(requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");
            var responseMessage = this.client.PostAsync(requestUri.ToString(), content).Result;
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = responseMessage.Content.ReadAsStreamAsync().Result)
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<string>(jsonReader);
            }
            }
        }
        
        /// <summary>
        /// 
        /// GET api/Tuple/Tuple8
        /// </summary>
        public async Task<System.Tuple<string, string, string, string, string, string, int, System.Tuple<string, string, string>>> GetTuple8Async()
        {
            var template = new System.UriTemplate("api/Tuple/Tuple8");
            var uriParameters = new System.Collections.Specialized.NameValueCollection();
            var requestUri = template.BindByName(this.baseUri, uriParameters);
            var responseMessage = await client.GetAsync(requestUri.ToString());
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = await responseMessage.Content.ReadAsStreamAsync())
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<System.Tuple<string, string, string, string, string, string, int, System.Tuple<string, string, string>>>(jsonReader);
            }
        }
        
        /// <summary>
        /// 
        /// GET api/Tuple/Tuple8
        /// </summary>
        public System.Tuple<string, string, string, string, string, string, int, System.Tuple<string, string, string>> GetTuple8()
        {
            var template = new System.UriTemplate("api/Tuple/Tuple8");
            var uriParameters = new System.Collections.Specialized.NameValueCollection();
            var requestUri = template.BindByName(this.baseUri, uriParameters);
            var responseMessage = this.client.GetAsync(requestUri.ToString()).Result;
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = responseMessage.Content.ReadAsStreamAsync().Result)
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<System.Tuple<string, string, string, string, string, string, int, System.Tuple<string, string, string>>>(jsonReader);
            }
        }
        
        /// <summary>
        /// 
        /// POST api/Tuple/Tuple8
        /// </summary>
        public async Task<string> PostTuple8Async(System.Tuple<string, string, string, string, string, string, string, System.Tuple<string, string, string>> tuple)
        {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create();
            requestSerializer.Serialize(requestWriter, tuple);
            var requestUri = new System.Uri(this.baseUri, "api/Tuple/Tuple8");
            var content = new StringContent(requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");
            var responseMessage = await client.PostAsync(requestUri.ToString(), content);
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = await responseMessage.Content.ReadAsStreamAsync())
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<string>(jsonReader);
            }
            }
        }
        
        /// <summary>
        /// 
        /// POST api/Tuple/Tuple8
        /// </summary>
        public string PostTuple8(System.Tuple<string, string, string, string, string, string, string, System.Tuple<string, string, string>> tuple)
        {
            using (var requestWriter = new System.IO.StringWriter())
            {
            var requestSerializer = JsonSerializer.Create();
            requestSerializer.Serialize(requestWriter, tuple);
            var requestUri = new System.Uri(this.baseUri, "api/Tuple/Tuple8");
            var content = new StringContent(requestWriter.ToString(), System.Text.Encoding.UTF8, "application/json");
            var responseMessage = this.client.PostAsync(requestUri.ToString(), content).Result;
            responseMessage.EnsureSuccessStatusCode();
            using (var stream = responseMessage.Content.ReadAsStreamAsync().Result)
            using (System.IO.StreamReader reader = new System.IO.StreamReader(stream))
            using (JsonReader jsonReader = new JsonTextReader(reader))
            {
            var serializer = new JsonSerializer();
            return serializer.Deserialize<string>(jsonReader);
            }
            }
        }


TypeScript client API codes:

        /** 
         * GET api/Tuple/Tuple7
         * @return {{Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:number, Item7:number}} 
         */
        GetTuple7(callback: (data : {Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:number, Item7:number}) => any){
            this.httpClient.get(encodeURI(this.baseUri + 'api/Tuple/Tuple7'), callback, this.error, this.statusCode);
        }

        /** 
         * POST api/Tuple/Tuple7
         * @param {{Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:number, Item7:number}} tuple 
         * @return {string} 
         */
        PostTuple7(tuple: {Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:number, Item7:number}, callback: (data : string) => any){
            this.httpClient.post(encodeURI(this.baseUri + 'api/Tuple/Tuple7'), tuple, callback, this.error, this.statusCode);
        }

        /** 
         * GET api/Tuple/Tuple8
         * @return {{Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:string, Item7:number, Rest:{Item1:string, Item2:string, Item3:string}}} 
         */
        GetTuple8(callback: (data : {Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:string, Item7:number, Rest:{Item1:string, Item2:string, Item3:string}}) => any){
            this.httpClient.get(encodeURI(this.baseUri + 'api/Tuple/Tuple8'), callback, this.error, this.statusCode);
        }

        /** 
         * POST api/Tuple/Tuple8
         * @param {{Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:string, Item7:string, Rest:{Item1:string, Item2:string, Item3:string}}} tuple 
         * @return {string} 
         */
        PostTuple8(tuple: {Item1:string, Item2:string, Item3:string, Item4:string, Item5:string, Item6:string, Item7:string, Rest:{Item1:string, Item2:string, Item3:string}}, callback: (data : string) => any){
            this.httpClient.post(encodeURI(this.baseUri + 'api/Tuple/Tuple8'), tuple, callback, this.error, this.statusCode);
        }


Remarks:
JsonConvert.DeserializeObject<T>(...) and ResponseMessage.Content.ReadAsAsync<T>(...) could handle well Tuple without injecting DefaultContractResolver in class WebApiConfig. However, because the serialized data has "m" prefix for each member property of tuple, the TypeScrip codes will have to access each member with the "m" prefix as well. Since version 1.5, WebApiClientGen uses JsonSerializer and stream to deserialize tuples as well as other data types.


Last edited Dec 29, 2015 at 8:16 PM by zijianhuang, version 4