Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modified serilization to support units saved as IComparable #200

Merged
merged 5 commits into from
Nov 2, 2016
Merged

Modified serilization to support units saved as IComparable #200

merged 5 commits into from
Nov 2, 2016

Conversation

eriove
Copy link
Contributor

@eriove eriove commented Oct 28, 2016

I had a class that I needed to serialize that had a unit as a IComparable. This pull requests makes this possible. I've tried testing other alternatives to not break anything. Let me know if you can think of any other use cases that should be tested.

Copy link
Owner

@angularsen angularsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an interesting idea and I can understand why this would be convenient. I am a little concerned we are putting too much magic in the serializer though, but I'm not too familiar with what are common practices in custom serializers and json.net. I personally would be a little afraid to rely on the type names for serialization, due to possible future breaking changes out of my control and how to deal with that, but I suppose that is up to the application to decide.

If I understand correctly, one must explicitly enable TypeNameHandling.Object/All/Auto both when serializing and deserializing in order for this to work, and then it "just works".

  • Can you extract the serializer setup code so that it is more clear that the test cases share identical serializer and deserializer settings?
  • Is there a reason for not using TypeNameHandling.Auto in both serialization and deserialization? From the docs it seems a reasonable one to use.
  • Could you add a test case where you serialize an object with three properties, of the same types you use in the existing cases?
  • Could you add a test case with a non-UnitsNet type implementing IComparable, that also has Unit and Value properties?

objectType == typeof(ValueUnit) ||
// All unit types implement IComparable
objectType==typeof(IComparable) ||
objectType.FullName.StartsWith("System." + nameof(IComparable)));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this string comparison required, doesn't the typeof check cover all cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was very much trial and error and a later refactoring made this unnecessary.

@eriove
Copy link
Contributor Author

eriove commented Oct 31, 2016

TypeNameHandling.Auto is needed for Json.Net to serialize the type information. By doing that no extra code is needed to handle deserialization of properties with interfaces. This is very convenient in combination with a plugin framework where each plugin requires it's own setting (implementing the same basic interface).

I've extracted the serializer code. Let me know if it is clear enough.

After refactoring my tests I realized that I didn't need TypeNameHandling.All.

Added the requested test cases, trying all different combinations with 3 IComparable.

Added the non UnitsNet IComparable.

Also added some nameof() to decrease the risk of name changes breaking the serialization. This could be done for more properties but that would require a specific unit to be referenced so I wasn't sure if that was the way to go.

Copy link
Owner

@angularsen angularsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting to look good, a couple of things still.

}
}

private static JsonSerializerSettings CreateJsonSerializerSettigns()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo

Assert.That(deserializedTestObject.Unit, Is.EqualTo("Test"));
}

[Test, TestCaseSource(nameof(TestObjectsForThreeObjectsInIComparableWithDifferentValues_ExpectAllCorrectlyDeserialized))]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part was slightly confusing to me, as I haven't used TestCaseSource before. If there is only one case, I think I would prefer a normal method that give me that TestObjWithThreeIComparable object.

Copy link
Contributor Author

@eriove eriove Nov 1, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will actually be 27 tests (testing all combinations of objects). Might be a little too much. I started with 3 very similar tests and did this to reduce code duplication and then it was easy to add the loop with all combinations. I have to admit that I don't fully understand the serialization so I rather have some extra tests just to be sure.

Copy link
Owner

@angularsen angularsen Nov 2, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, ok. 27 tests seem a bit overboard, yes. Tests are nice, but I want each test to bring value and from the way I understand the implementation design and the test, I don't see how testing multiple variations of TestObjWithThreeIComparable will help us avoid bugs here. I think I'd rather have one or possibly two test cases with different combinations, but that should be quite enough, I think.

Their docs helped me understand what goes on: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm

Basically they add a $type prop that describes the actual object type when it was serialized, then the deserializer tries to use the exact same type by its assembly name, and full type name.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var deserializedTestObject = JsonConvert.DeserializeObject<TestObjWithThreeIComparable>(json, jsonSerializerSettings);

Assert.That(deserializedTestObject.Value1.GetType(), Is.EqualTo(comparable1.GetType()));
Assert.That(((deserializedTestObject.Value1)), Is.EqualTo(comparable1));
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant parantheses.

Assert.That(((deserializedTestObject.Value3)), Is.EqualTo(comparable3));
}

public static object[] TestObjectsForThreeObjectsInIComparableWithDifferentValues_ExpectAllCorrectlyDeserialized
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be private.

}

internal class TestObj
{
public Frequency? NullableFrequency { get; set; }
public Frequency NonNullableFrequency { get; set; }
}

internal class TestObjWithValueAndUnit : IComparable
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All these types can be private, can't they?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true. Followed the existing class, will change all of them.

};
JsonSerializerSettings jsonSerializerSettings = CreateJsonSerializerSettigns();

string json = JsonConvert.SerializeObject(testObjWithIComparable,jsonSerializerSettings);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add space between params.

@eriove
Copy link
Contributor Author

eriove commented Nov 1, 2016

Fixed the typos and formatting. Kept the TestCaseSource. Let's discuss further.

@angularsen
Copy link
Owner

Will try to get to this tomorrow.

On Tue, Nov 1, 2016, 10:39 Erik Ovegård notifications@github.com wrote:

Fixed the typos and formatting. Kept the TestCaseSource. Let's discuss
further.


You are receiving this because you commented.

Reply to this email directly, view it on GitHub
#200 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAwFaDjRxBAGftPYw-6rla7RXbjIXjbvks5q5wjEgaJpZM4KjW2r
.

@eriove
Copy link
Contributor Author

eriove commented Nov 2, 2016

I removed the TestCaseSource and just kept one test that tests with TestObjWithThreeIComparable.

Copy link
Owner

@angularsen angularsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. I think this is ready for a new alpha nuget. Serialization is still kind of experimental, represented by the prerelease nuget.

@angularsen angularsen merged commit 52c8dba into angularsen:master Nov 2, 2016
@angularsen
Copy link
Owner

Nuget 1.0.0-alpha5 is now out.

@eriove eriove deleted the feature/SerilizationOfIComparable branch November 2, 2016 14:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants