Skip to content

Commit 83907d7

Browse files
committed
Implemented serialization attribute overrides. Incomplete--you can set an override of JsonIgnore but you can't un-ignore a property, for instance.
1 parent bb33c6c commit 83907d7

5 files changed

Lines changed: 93 additions & 4 deletions

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"posts": {
3+
"id": "2",
4+
"title": "How to fry an egg",
5+
"links": {
6+
"author": "5"
7+
}
8+
},
9+
"linked": {
10+
"users": [
11+
{
12+
"id": "5",
13+
"name": "Bob"
14+
}
15+
]
16+
}
17+
}

JSONAPI.Tests/JSONAPI.Tests.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,9 @@
109109
<None Include="Data\NonStandardIdTest.json">
110110
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
111111
</None>
112+
<None Include="Data\OverrideSerializationAttributesTest.json">
113+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
114+
</None>
112115
<None Include="Data\SerializerIntegrationTest.json">
113116
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
114117
</None>

JSONAPI.Tests/Json/LinkTemplateTests.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,33 @@ public void GetResourceWithLinkTemplateRelationship()
6161
var expected = JsonHelpers.MinifyJson(File.ReadAllText("LinkTemplateTest.json"));
6262
var output = Encoding.ASCII.GetString(stream.ToArray());
6363
Trace.WriteLine(output);
64-
Assert.AreEqual(output.Trim(), expected);
64+
Assert.AreEqual(expected,output.Trim());
65+
}
66+
67+
[TestMethod]
68+
[DeploymentItem(@"Data\OverrideSerializationAttributesTest.json")]
69+
public void OverrideSerializationAttributesTest()
70+
{
71+
// Arrange
72+
var formatter = new JsonApiFormatter
73+
(
74+
new JSONAPI.Core.PluralizationService()
75+
);
76+
var stream = new MemoryStream();
77+
78+
// Act
79+
JSONAPI.Core.MetadataManager.Instance.SetPropertyAttributeOverrides(
80+
ThePost, typeof(Post).GetProperty("Author"),
81+
new SerializeAs(SerializeAsOptions.Ids),
82+
new IncludeInPayload(true)
83+
);
84+
formatter.WriteToStreamAsync(typeof(Post), ThePost, stream, null, null);
85+
86+
// Assert
87+
var expected = JsonHelpers.MinifyJson(File.ReadAllText("OverrideSerializationAttributesTest.json"));
88+
var output = Encoding.ASCII.GetString(stream.ToArray());
89+
Trace.WriteLine(output);
90+
Assert.AreEqual(expected, output.Trim());
6591
}
6692
}
6793
}

JSONAPI/Core/MetadataManager.cs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ public sealed class MetadataManager
1313
private class PropertyMetadata
1414
{
1515
public bool PresentInJson { get; set; } // only meaningful for incoming/deserialized models!
16-
public Lazy<IEnumerable<System.Attribute>> AttributeOverrides
17-
= new Lazy<IEnumerable<System.Attribute>>(
18-
() => new List<System.Attribute>()
16+
public Lazy<ISet<System.Attribute>> AttributeOverrides
17+
= new Lazy<ISet<System.Attribute>>(
18+
() => new HashSet<System.Attribute>()
1919
);
2020
}
2121

@@ -103,5 +103,46 @@ public bool PropertyWasPresent(object deserialized, PropertyInfo prop)
103103
return this.GetMetadataForProperty(deserialized, prop).PresentInJson;
104104
}
105105

106+
/// <summary>
107+
/// Set different serialization attributes at runtime than those that were declared on
108+
/// a property at compile time. E.g., if you declared a relationship property with
109+
/// [SerializeAs(SerializeAsOptions.Link)] but you want to change that to
110+
/// SerializeAsOptions.Ids when you are transmitting only one object, you can do:
111+
///
112+
/// MetadataManager.SetPropertyAttributeOverrides(
113+
/// theModelInstance, theProperty,
114+
/// new SerializeAsAttribute(SerializeAsOptions.Ids)
115+
/// );
116+
///
117+
/// Further, if you want to also include the related objects in the serialized document:
118+
///
119+
/// MetadataManager.SetPropertyAttributeOverrides(
120+
/// theModelInstance, theProperty,
121+
/// new SerializeAs(SerializeAsOptions.Ids),
122+
/// new IncludeInPayload(true)
123+
/// );
124+
///
125+
/// Calling this function resets all overrides, so calling it twice will result in only
126+
/// the second set of overrides being applied. At present, the order of the attributes
127+
/// is not meaningful.
128+
/// </summary>
129+
/// <param name="model">The model object that is to be serialized, for which you want to change serialization behavior.</param>
130+
/// <param name="prop">The property for which to override attributes.</param>
131+
/// <param name="attrs">One or more attribute instances that will override the declared behavior.</param>
132+
public void SetPropertyAttributeOverrides(object model, PropertyInfo prop, params System.Attribute[] attrs)
133+
{
134+
var aoverrides = this.GetMetadataForProperty(model, prop).AttributeOverrides.Value;
135+
lock (aoverrides)
136+
{
137+
aoverrides.Clear();
138+
aoverrides.UnionWith(attrs);
139+
}
140+
}
141+
142+
internal IEnumerable<System.Attribute> GetPropertyAttributeOverrides(object model, PropertyInfo prop)
143+
{
144+
return this.GetMetadataForProperty(model, prop).AttributeOverrides.Value;
145+
}
146+
106147
}
107148
}

JSONAPI/Json/JsonApiFormatter.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,8 @@ protected void Serialize(object value, Stream writeStream, JsonWriter writer, Js
252252
SerializeAsOptions sa = SerializeAsOptions.Ids;
253253

254254
object[] attrs = prop.GetCustomAttributes(true);
255+
// aha...this way the overrides will be applied last!
256+
attrs = attrs.Concat(MetadataManager.Instance.GetPropertyAttributeOverrides(value, prop)).ToArray();
255257

256258
foreach (object attr in attrs)
257259
{

0 commit comments

Comments
 (0)