Closed vamseemudradi7 closed 5 years ago
Alright, this one is a bit tricky.
I'm assuming that we want to have resources serialized like
<?xml version="1.0"?>
<account>
<account_number>12345</account_number>
<balance currency="usd">100.00</balance>
<link rel="deposit" href="/accounts/12345/deposit" />
<link rel="withdraw" href="/accounts/12345/withdraw" />
<link rel="transfer" href="/accounts/12345/transfer" />
<link rel="close" href="/accounts/12345/close" />
</account>
and not
<?xml version="1.0"?>
<account>
<account_number>12345</account_number>
<balance currency="usd">100.00</balance>
<links>
<link rel="deposit" href="/accounts/12345/deposit" />
<link rel="withdraw" href="/accounts/12345/withdraw" />
<link rel="transfer" href="/accounts/12345/transfer" />
<link rel="close" href="/accounts/12345/close" />
</links>
</account>
since that appears to be the most widely spread approach.
You see, it turns out that while serializing a class, you can't skip wrapping a property into its own XML element unless that property implements IEnumerable. (or at least I couldn't find a way to do so)
If we use SerializableDictionary
, we will always get the second result.
It seems that the simplest approach to getting a proper result would be to wrap the current links dictionary into a LinkCollection
. E.g.
public class LinkCollection : IEnumerable<Link>
{
private readonly Dictionary<string, Link> links = new Dictionary<string, Link>();
public int Count { get; set; }
public void Add(string name, Link link) =>
links.Add(name, link);
[Obsolete("This method is here only for the XML serialization to work. It should not be used.", true)]
public void Add(Link link) =>
throw new NotImplementedException();
public bool ContainsKey(string key) =>
links.ContainsKey(key);
public IEnumerator<Link> GetEnumerator() =>
links.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() =>
GetEnumerator();
public Link this[string name]
{
get => links[name];
set => links[name] = value;
}
}
This will get us what we want but it comes with another downside.
XmlSerializer
will not work on types implementing IEnumerable
if they don't publicly expose an Add(object obj)
method.
This means we must expose an Add(Link link)
method.
Fortunately, the ObsoleteAttribute
allows us to emit a compile-time error if someone were to use it, so besides being kind of ugly, it doesn't bring any other problems.
Switch Dictionary<string, Link>
with LinkCollection
and you now get proper serialization in the form of
<?xml version="1.0" encoding="UTF-8"?>
<TestLinkContainer xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<link rel="test" hrev="test" method="GET" />
<link rel="test" hrev="test" method="GET" />
<link rel="test" hrev="test" method="GET" />
<link rel="test" hrev="test" method="GET" />
<Id>123</Id>
</TestLinkContainer>
This is only at first sight. If you have any other ideas, please share. Otherwise, if @jam13c approves of this approach, I could dig deeper and submit a PR.
Need to solve this through use of SerializableDictionary (instead of using Dictionary) with the help of https://stackoverflow.com/questions/67959/net-xml-serialization-gotchas or https://msdn.microsoft.com/en-us/library/gg496181.aspx in your source code in LinkContainer.cs and ILinkContainer.cs