{"action":"created","issue":{"url":"https://api.github.com/repos/OData/WebApi/issues/438","labels_url":"https://api.github.com/repos/OData/WebApi/issues/438/labels{/name}","comments_url":"https://api.github.com/repos/OData/WebApi/issues/438/comments","events_url":"https://api.github.com/repos/OData/WebApi/issues/438/events","html_url":"https://github.com/OData/WebApi/issues/438","id":95715568,"number":438,"title":"Serialize content of Dictionary<string, string>","user":{"login":"Casimodo72","id":10551738,"avatar_url":"https://avatars.githubusercontent.com/u/10551738?v=3","gravatar_id":"","url":"https://api.github.com/users/Casimodo72","html_url":"https://github.com/Casimodo72","followers_url":"https://api.github.com/users/Casimodo72/followers","following_url":"https://api.github.com/users/Casimodo72/following{/other_user}","gists_url":"https://api.github.com/users/Casimodo72/gists{/gist_id}","starred_url":"https://api.github.com/users/Casimodo72/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Casimodo72/subscriptions","organizations_url":"https://api.github.com/users/Casimodo72/orgs","repos_url":"https://api.github.com/users/Casimodo72/repos","events_url":"https://api.github.com/users/Casimodo72/events{/privacy}","received_events_url":"https://api.github.com/users/Casimodo72/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/OData/WebApi/labels/Open%20for%20contribution","name":"Open for contribution","color":"5319e7"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-07-17T18:29:04Z","updated_at":"2015-08-09T22:46:28Z","closed_at":null,"body":"Hi,\r\n\r\nI have an entity with a property of type Dictionary<**string**, **string**>. Existing data in the dictionary is not serialized in responses.\r\nNow, I don't know if such dictionaries are intended to be supported at all, but the dictionary property shows up nicely in the EDM so I thought this could work, but it doesn't. This is not related to open types, I just need a string dictionary.\r\nCan someone clarify?\r\n\r\nScenario:\r\n```csharp\r\npublic partial class Zappa\r\n{\r\n\tpublic Zappa()\r\n\t{\r\n\t    Header = new Dictionary<string, string>();\r\n\t}\r\n\t\r\n\tpublic Guid Id { get; set; }\r\n\tpublic Dictionary<string, string> Header { get; set; }\r\n}\r\n```\r\n```xml\r\n<EntityType Name=\"Zappa\" BaseType=\"Ga.Data.EntityBase\">\r\n\t-<Key>\r\n\t\t<PropertyRef Name=\"Id\"/>\r\n\t</Key>\r\n\t<Property Name=\"Id\" Type=\"Edm.Guid\"/>\r\n\t<Property Name=\"Header\" Type=\"Collection(System.Collections.Generic.KeyValuePair_2OfString_String)\"/>\t\r\n</EntityType>\r\n```\r\nIn controller:\r\n```csharp\r\nitem.Header = new Dictionary<string, string>();\r\nitem.Header.Add(\"Hello\", \"Is this OK?\");\r\n```\r\nResponse data:\r\n```json\r\n{\r\n    \"@odata.context\":\"http://localhost:26162/odata/$metadata#Zappa/$entity\",\r\n    \"Id\":\"df45be0e-cab8-4f90-aeb3-7f9b5608aaff\",\"Header\":[\r\n    {\r\n      \r\n    }]\r\n}\r\n```\r\nI'm using:\r\nMicrosoft.AspNet.OData 5.7.0-beta-150716-Nightly (and the latest nightly ODataLib packages)\r\n.NET 4.5.2\r\nVS 2015 RC\r\n\r\nRegards,\r\nKasimier Buchcik"},"comment":{"url":"https://api.github.com/repos/OData/WebApi/issues/comments/129258269","html_url":"https://github.com/OData/WebApi/issues/438#issuecomment-129258269","issue_url":"https://api.github.com/repos/OData/WebApi/issues/438","id":129258269,"user":{"login":"Casimodo72","id":10551738,"avatar_url":"https://avatars.githubusercontent.com/u/10551738?v=3","gravatar_id":"","url":"https://api.github.com/users/Casimodo72","html_url":"https://github.com/Casimodo72","followers_url":"https://api.github.com/users/Casimodo72/followers","following_url":"https://api.github.com/users/Casimodo72/following{/other_user}","gists_url":"https://api.github.com/users/Casimodo72/gists{/gist_id}","starred_url":"https://api.github.com/users/Casimodo72/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Casimodo72/subscriptions","organizations_url":"https://api.github.com/users/Casimodo72/orgs","repos_url":"https://api.github.com/users/Casimodo72/repos","events_url":"https://api.github.com/users/Casimodo72/events{/privacy}","received_events_url":"https://api.github.com/users/Casimodo72/received_events","type":"User","site_admin":false},"created_at":"2015-08-09T22:46:28Z","updated_at":"2015-08-09T22:46:28Z","body":"I just experimented a bit with dictionaries. I don't know yet what the outcome actually means for me, because it is getting late and I need some sleep.\r\n\r\nScenario type:\r\n```csharp\r\n[DataContract]\r\npublic class Salutation\r\n{\r\n\t[DataMember]\r\n        public Dictionary<string, string> MyAttributes1 { get; set; } = new Dictionary<string, string>();\r\n\t\r\n\t[DataMember]\r\n        public ODataNamedValueDictionary<string> MyAttributes2 { get; set; } = new ODataNamedValueDictionary<string>();        \r\n}\r\n```\r\n\r\n1) ```Dictionary<string, string>```:\r\nSurprise, this is already supported.\r\nOne only needs to register the type explicitely:\r\nIt would be great if OData Web Api could make this easier and one could avoid registering each generic form of this type.\r\n```csharp\r\nbuilder.AddComplexType(typeof(Dictionary<string, string>));\r\n```\r\nThe generated schema:\r\n```xml\r\n<Schema xmlns=\"http://docs.oasis-open.org/odata/ns/edm\" Namespace=\"System.Collections.Generic\">\r\n  <ComplexType Name=\"Dictionary_2OfString_String\">\r\n    <Property Name=\"Keys\" Type=\"Collection(Edm.String)\"/>\r\n    <Property Name=\"Values\" Type=\"Collection(Edm.String)\"/>\r\n  </ComplexType>\r\n</Schema>\r\n```\r\nThe data being generated:\r\n```json\r\n\"MyAttributes1\":{\r\n\t\"Keys\":[\r\n\t\t\"Hello\"\r\n\t],\"Values\":[\r\n\t\t\"Is this OK?\"\r\n\t]\r\n}\r\n```\r\n2) I implemented IDictionary<string, TValue> on the type ODataNamedValueDictionary and used the open type dynamic property container as its underlying storage.\r\nHere I also must register the type, otherwise no data is serialized.\r\nIt would be great if OData Web Api could make this easier and one could avoid registering each generic form of this type.\r\nI also have to make the underlying container property public, which is not nice.\r\n```csharp\r\nbuilder.AddComplexType(typeof(ODataNamedValueDictionary<string>));\r\n```\r\nThis produces the following data. That's exactly what I hoped for.\r\n```json\r\n\"MyAttributes1\":{\r\n\t\"Hello\":\"Is this OK?\"\r\n}\r\n```\r\nODataNamedValueDictionary:\r\n```csharp\r\n[DataContract]\r\npublic class ODataNamedValueDictionary<TValue> : IDictionary<string, TValue>\r\n{\r\n\t[DataMember]\r\n\t// NOTE: Must be public, otherwise OData Web Api won't serialize anything.\r\n\tpublic IDictionary<string, object> Items { get { return _items ?? (_items = new Dictionary<string, object>()); } set { _items = value; } }\r\n\tIDictionary<string, object> _items;\r\n\r\n\tpublic TValue this[string key]\r\n\t{\r\n\t    get { return (TValue)Items[key]; }\r\n\t    set { Items[key] = value; }\r\n\t}\r\n\r\n\tpublic int Count\r\n\t{\r\n\t    get { return Items.Count; }\r\n\t}\r\n\r\n\tpublic bool IsReadOnly\r\n\t{\r\n\t    get { return Items.IsReadOnly; }\r\n\t}\r\n\r\n\tpublic ICollection<string> Keys\r\n\t{\r\n\t    get { return Items.Keys; }\r\n\t}\r\n\r\n\t/// <summary>\r\n\t/// NOTE: This method will create a new ReadOnlyCollection based on the\r\n\t/// values of the underlying dictionary.\r\n\t/// </summary>\r\n\tpublic ICollection<TValue> Values\r\n\t{\r\n\t    get { return new ReadOnlyCollection<TValue>(Items.Values.Cast<TValue>().ToList()); }\r\n\t}\r\n\r\n\tpublic void Add(KeyValuePair<string, TValue> item)\r\n\t{\r\n\t    Items.Add(Convert(item));\r\n\t}\r\n\r\n\tpublic void Add(string key, TValue value)\r\n\t{\r\n\t    Items.Add(key, value);\r\n\t}\r\n\r\n\tpublic void Clear()\r\n\t{\r\n\t    Items.Clear();\r\n\t}\r\n\r\n\tpublic bool Contains(KeyValuePair<string, TValue> item)\r\n\t{\r\n\t    return Items.Contains(Convert(item));\r\n\t}\r\n\r\n\tpublic bool ContainsKey(string key)\r\n\t{\r\n\t    return Items.ContainsKey(key);\r\n\t}\r\n\r\n\tpublic void CopyTo(KeyValuePair<string, TValue>[] array, int arrayIndex)\r\n\t{\r\n\t    if (array == null) throw new ArgumentNullException(nameof(array));\r\n\t    if (arrayIndex < 0) throw new ArgumentOutOfRangeException(nameof(arrayIndex));\r\n\t    if (Items.Count > array.Length - arrayIndex)\r\n\t\tthrow new ArgumentException(\"The number of elements in the source dictionary \" +\r\n\t\t    \"is greater than the available space from arrayIndex to the end of the destination array.\",\r\n\t\t    nameof(arrayIndex));\r\n\r\n\t    var i = 0;\r\n\t    foreach (var item in Items)\r\n\t    {\r\n\t\tarray[i] = Convert(item);\r\n\t\ti++;\r\n\t    }\r\n\t}\r\n\r\n\tpublic bool Remove(KeyValuePair<string, TValue> item)\r\n\t{\r\n\t    return Items.Remove(Convert(item));\r\n\t}\r\n\r\n\tpublic bool Remove(string key)\r\n\t{\r\n\t    return Items.Remove(key);\r\n\t}\r\n\r\n\tpublic bool TryGetValue(string key, out TValue value)\r\n\t{\r\n\t    object obj;\r\n\t    if (Items.TryGetValue(key, out obj))\r\n\t    {\r\n\t\tvalue = (TValue)obj;\r\n\t\treturn true;\r\n\t    }\r\n\r\n\t    value = default(TValue);\r\n\t    return false;\r\n\t}\r\n\r\n\tpublic IEnumerator<KeyValuePair<string, TValue>> GetEnumerator()\r\n\t{\r\n\t    foreach (var item in Items)\r\n\t\tyield return Convert(item);\r\n\t}\r\n\r\n\tIEnumerator IEnumerable.GetEnumerator()\r\n\t{\r\n\t    return GetEnumerator();\r\n\t}\r\n\r\n\tKeyValuePair<string, object> Convert(KeyValuePair<string, TValue> item)\r\n\t{\r\n\t    return new KeyValuePair<string, object>(item.Key, item.Value);\r\n\t}\r\n\r\n\tKeyValuePair<string, TValue> Convert(KeyValuePair<string, object> item)\r\n\t{\r\n\t    return new KeyValuePair<string, TValue>(item.Key, (TValue)item.Value);\r\n\t}\r\n}\r\n```\r\n"}}