I am able to serialize proxy objects using below code:
public class NHibernateContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(NHibernate.Proxy.INHibernateProxy).IsAssignableFrom(objectType))
return base.CreateContract(objectType.BaseType);
return base.CreateContract(objectType);
}
}
But how can I make JSON.NET ignore the NHibernate Proxy objects during serialization.
The problem I am facing is that, the parent object is fetching 1000's of child object, where as I want to send JSON only for the parent object, so I want to ignore proxy object and fetch only eager loaded relationships.
And if I comment above code, then I get the error for JSON.NET failing to serialize the proxy objects.
Please help!
write a dummy class like this.
public class NhProxyJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteNull();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(INHibernateProxy).IsAssignableFrom(objectType);
}
}
Related
I'm working with .Net Core MVC 2.1
In our views we have price with thousand separator like 2,500,000
I want to get all of them in our back-end models as INT type, I mean without ,.
I want to write something like a middleware to do that without ModelBinder attribute for each int property.
How can we do it?
Create a class derrived from JsonConverter, similar to this (might need more validation in ReadJson):
public class FormattedStringToIntConverter : JsonConverter
{
public override bool CanRead => true;
public override bool CanWrite => false;
public override bool CanConvert(Type objectType) => objectType == typeof(string);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var token = JToken.Load(reader);
int.TryParse(token.ToString(), NumberStyles.AllowThousands, CultureInfo.CreateSpecificCulture("en-US"), out var value);
return value;
}
}
Now you can add an attribute to your model properties where you want to use this converter:
[JsonConverter(typeof(FormattedStringToIntConverter))]
Or if you want to check all strings, you can change your Startup.cs to include some JsonOptions:
services.AddMvc().AddJsonOptions(o => {
o.SerializerSettings.Converters.Add(new FormattedStringToIntConverter());
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
You may add more logic to CanConvert to only try to convert strings that really contain a number.
I need to serialize one field to JSON and store it in database. For this, I implement IUserType interface. However, due to testing purposes, I'd like to pass a serializer interface in a constructor parameter. Ideally, I want to achieve that by telling NH to create an instance of my IUserType implementation using Ninject. Is this possible at all?
Cfg.Environment.BytecodeProvider.ObjectsFactory is responsible for creating objects used by NHibernate. You can implement IBytecodeProvider to inject your own for example:
class MyBytecodeProvider : NHibernate.Bytecode.Lightweight.BytecodeProviderImpl, IObjectsFactory
{
public override IObjectsFactory ObjectsFactory
{
get { return this; }
}
#region IObjectsFactory implementation
public object CreateInstance(System.Type type)
{
// TODO:
}
public object CreateInstance(System.Type type, bool nonPublic)
{
// TODO:
}
public object CreateInstance(System.Type type, params object[] ctorArgs)
{
// TODO:
}
#endregion
}
UPDATE
Found the issue -- was inheriting from wrong class, needed to be JsonConverter.
I have a class that has a Location property of type System.Data.Entity.Spatial.DbGeography. The default Json.NET serializer puts out JSON text like this:
...
"PlaceType": 0,
"Location": {
"Geography": {
"CoordinateSystemId": 4326,
"WellKnownText": "POINT (-88.00000 44.00000)"
}
},
"AddedDT": null,
...
I want it to put out text like this:
...
"PlaceType": 0,
"Location": [-88.00000,44.00000],
"AddedDT": null,
...
...so it seems to me what I should do would be to override whatever converter is currently being used on the DbGeography type.
The examples I've seen so far that use CustomCreationConverters and ContractResolvers seem to address how you'd replace the serializer for the main class being serialized, not for a type that's only a property of that class. The examples that involve annotating the class that's being overridden don't work for me because I don't define DbGeography in my code and it's effectively a sealed class because it has no constructor and can only be instantiated by internal factory methods.
Is there a way to apply a JsonConverter to a type fluently? If so, what would the converter look like? Do I just override the WriteJson() method?
You can add a custom serializer to a single attribute like this:
public class Comment
{
public string Author { get; set; }
[JsonConverter(typeof(NiceDateConverter))]
public DateTime Date { get; set; }
public string Text { get; set; }
}
public class NiceDateConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var date = (DateTime) value;
var niceLookingDate = date.ToString("MMMM dd, yyyy 'at' H:mm tt");
writer.WriteValue(niceLookingDate);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter.");
}
public override bool CanRead
{
get { return false; }
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(DateTime);
}
}
Then, when you serialize your object with JsonConvert.SerializeObject(), the custom serializer will be used for the Date property.
Turns out I just needed to inherit from JsonConverter instead of CustomCreationConverter, and everything else I was trying to change was OK all along.
I'm still not sure if there's a way to apply the JsonConverter fluently, but there is another way to apply the JsonConverter without referencing Json.NET in your domain/core project or marking up your domain classes with references to a peripheral library:
var jsonSerializer = new JsonSerializer();
jsonSerializer.Converters.Add(new DbGeographyConverter());
jsonSerializer.Serialize(jsonWriter, place);
Use the JsonConverterAttribute on the property and define a custom converter-
for example, we have a property that comes in as a unix value (long int) and we serialize it to a .Net DateTime:
[JsonConverter(typeof(UnixTimeJsonConverter))]
public DateTime Requested { get; set; }
I am attempting to generate a JSON file that will be used within the Dojo javascript framework and would like to return a position attribute to be used in a dojo.place() call. The position parameter can be either a number or a string.
Using the StructLayoutwould not seem to work as-is since the serializer would try to emit both the String and Integer types. I'm looking at creating a custom ContractResolver that overrides the CreatePrimitiveContract to return a custom JsonConverter class. However, looking a the API, it appears that the JsonConverter is created based on type, and not a specific object value.
How can I handle this case in C# using the Json.NET serializer?
Presumably the solution would involve two properties with custom setters to null out the other property when one is set in conjunction with some sort of custom Json.Net class to inspect the values of the properties and only serialize the non-null one.
** Hypothetical Example **
// C# struct (or class)
[StructLayout(LayoutKind.Explicit)]
struct DojoPosition {
[JsonProperty(PropertyName="position")]
[FieldOffset(0)]
public String StrPos;
[JsonProperty(PropertyName="position")]
[FieldOffset(0)]
public Int32 IntPos;
}
// Serialization output
DojoPosition pos;
pos.StrPos = "only";
var output = JsonConvert.SerializeObject(pos);
// Output is: { "position": "only" }
pos.IntPos = 3;
var output = JsonConvert.SerializeObject(pos);
// Output is: { "position": 3 }
I just had a similiar problem.
For simple manipulation of a contract look there: Overriding the serialization behaviour in Json.Net
For resolving a JsonPrimitiveContract override the CreateContract method.
Here is an example based on our solution:
public class JsonDotNetContractResolver : DefaultContractResolver
{
protected override JsonContract CreateContract(Type objectType)
{
if (typeof(DojoPosition).IsAssignableFrom(objectType))
{
return new JsonPrimitiveContract(objectType.GetGenericArguments()[1])
{
CreatedType = typeof(object), // Not sure this will work for you, or is necessary...
IsReference = false,
Converter = DojoPositionConverter,
};
}
return base.CreateContract(objectType);
}
private class DojoPositionConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var dp = (DojoPosition) value;
if(string.IsNullOrEmpty(dp.StrPos))
serializer.Serialize(writer,dp.IntPos);
else
serializer.Serialize(writer,dp.StrPos);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//...
}
public override bool CanConvert(Type objectType)
{
//....
}
}
}
How to determine the type to deserialize from the reader is your homework ;)
Using the method described in NHibernate & INotifyPropertyChanged, the repository will return a collection of proxies that implement INotifyPropertyChanged, but on some objects when saving or deleting it will throw an error:
at NHibernate.Impl.SessionFactoryImpl.GetEntityPersister(String entityName)
at NHibernate.Impl.SessionImpl.GetEntityPersister(String entityName, Object obj)
at NHibernate.Engine.ForeignKeys.IsTransient(String entityName, Object entity, Nullable`1 assumed, ISessionImplementor session)
at NHibernate.Event.Default.AbstractSaveEventListener.GetEntityState(Object entity, String entityName, EntityEntry entry, ISessionImplementor source)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
at MyCode ...
I figured out that if I create the session without the interceptor the SaveOrUpdate works fine, but with the interceptor it errors.
with the interceptor:
public ISession GetSession(ISessionFactory factory)
{
IInterceptor dataBinding = new DataBindingInterceptor {SessionFactory = factory};
return factory.OpenSession(dataBinding);
}
without
public ISession GetSession(ISessionFactory factory)
{
return factory.OpenSession();
}
I'm at a loss for how to go about even figuring out why the interceptor would break the save.
The only change I made to the code was changing the line
Type type = Type.GetType(clazz);
to
Type type = FindType(clazz);
public Type FindType(string typeName)
{
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
Type foundType = assembly.GetType(typeName);
if (foundType != null)
return foundType;
}
return null;
}
Solution was to always use the session with the interceptor. I was creating the IList with the interceptor, but saving with a generic session. This bypassed the GetEntityName override which redirected the proxy to the correct persister.