Skip to content
fonlow edited this page Jun 3, 2017 · 5 revisions

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 "m_Item1", 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(...)}} and {{ResponseMessage.Content.ReadAsAsync(...)}} 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.

Clone this wiki locally