Properties Added with Aspects Not in Generated JSON Schema - json.net

I'm trying to generate JSON Schemas using Newtonsoft JSON Schema. Regular properties added in my POCO class are added to the schema when it is generated. I'm also using PostSharp Aspects to add properties, but none of those are added to the schema.
This is a Console application targeting .NET 4.7.2.
Here is my Console application:
public class Program
{
private static void Main(string[] args)
{
JSchemaGenerator gen = new JSchemaGenerator();
JSchema schema = gen.Generate(typeof(MyClass));
File.WriteAllText("C:\\Temp\\TestSchema.json", schema.ToString());
}
}
Here is my aspect:
[PSerializable]
public class TestAspect : InstanceLevelAspect
{
[IntroduceMember]
[JsonProperty(Required = Required.Always)]
public string AspectProp { get; set; }
}
And here is my POCO:
[TestAspect]
public class MyClass
{
public int MyProperty { get; set; }
}
Finally, here is the generated schema:
{
"type": "object",
"properties": {
"MyProperty": {
"type": "integer"
}
},
"required": [
"MyProperty"
]
}
The MyProperty property is in the schema, but AspectProp - the property added by the aspect - is not.
When I open the exe in a decompiler, I can see that AspectProp is actually added to MyClass:
I'm not sure if this is a problem with PostSharp or Newtonsoft JSON Schema or if I'm doing something wrong. It seems like this should be possible.
Edit 1: 20 May
I split my solution out into separate projects - one for the Console app, one for the Aspect and one for MyClass. After making sure I was referencing the generated MyClass DLL directly (i.e. not a project reference, I actually removed the project once MyClass was built) it did not make a difference. AspectProp is still not in the schema. Based on this and the serialization suggested below by #dbc, this leads me to believe it is a problem with the Newtonsoft schema generator
Edit 2: 20 May
Per Antonin's Answer below, I created a new ContractResolver:
public class AspectPropertyResolver : DefaultContractResolver
{
public AspectPropertyResolver()
{
SerializeCompilerGeneratedMembers = true;
}
}
And then registered it in my app before calling Generate:
gen.ContractResolver = new AspectPropertyResolver();
Now my schema includes the aspect-generated property.

Newtonsoft.Json has an opt-in feature to serialize compiler-generated properties. See Newtonsoft.Json.Serialization.DefaultContractResolver.SerializeCompilerGeneratedMembers property.

Related

Unable to resolve dead simple configuration dependency

I am trying to set up dependency injection in a small blazor-based web app.
My current code is:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
AddDeliveryStats(builder.Services, builder.Configuration);
// ...blazor template things...
void AddDeliveryStats(IServiceCollection services, ConfigurationManager config)
{
services.Configure<BigQuerySettings>(config.GetSection("BigQuery"));
services.AddTransient<IBigQueryClient, BigQueryClient>();
// ...other stuff not pertinent to the error...
}
where BigQuerySettings is given as
public class BigQuerySettings
{
public string ProjectId { get; set; }
public string DataSetId { get; set; }
public string AuthFilePath { get; set; }
}
and BigQueryClient has the following constructor:
public BigQueryClient(
BigQuerySettings bigQuerySettings,
ILogger<BigQueryClient> logger) { /* ... */ }
and my appsettings.json contains the following:
{
// ...
"BigQuery": {
"ProjectId": "<project-identifier>",
"DataSetId": "",
"AuthFilePath": "BigQueryAuthProd.json"
}
}
and if this looks pretty much like a tutorial example, that's because it basically is. It does not work and it is not obvious why. I get the following error:
Some services are not able to be constructed
(Error while validating the service descriptor 'ServiceType: IBigQueryClient Lifetime: Transient ImplementationType: BigQueryClient': Unable to resolve service for type 'BigQuerySettings' while attempting to activate 'BigQueryClient'.)
I have copied this code from online tutorial examples and adapted it as appropriate to my own classes, and I have read every piece of documentation I have been able to find (much of which I can't understand) and googled at least ten different permutations of the keywords in the error message. Nothing really points to what I am doing wrong.
By default, a call to services.Configure will only allow injecting an IOption<BigQuerySettings> into your consumers.
If, however, you wish to inject BigQuerySettings directly into your consumer (which I would argue you should), you should do the following:
BigQuerySettings settings =
Configuration.GetSection("BigQuery").Get<BigQuerySettings>();
// TODO: Verify settings here (if required)
// Register BigQuerySettings as singleton in the container.
services.AddSingleton<BigQuerySettings>(settings);
This allows BigQuerySettings to be injected into BigQueryClient.

ConfigurationManager.AppSettings.Get(key).ConvertTo error in asp.net core

Below is my code. I try to convert below code from asp.net to asp.net core. But in asp.net core in last line ConvertTo is showing error because Get(key) does not have definition of ConvertTo. Don't know what is the problem.
I am unable to find any solution how can i write below code in asp.net core?
public static T Get<T>(string key)
{
if (!Exists(key))
{
throw new ArgumentException(String.Format("No such key in the AppSettings: '{0}'", key));
}
return ConfigurationManager.AppSettings.Get(key).ConvertTo<T>(new CultureInfo("en-US"));
}
Thanks in advance.
I suggest you carefully reading the documentation first. In .NET Core the way how we work with configuration is changed significantly ( different source using, mapping to POCO objects , etc).
In your case you may simply use ConfigurationBinder’s GetValue<T> extension method instead of implementing own method for value conversion:
IConfiguration.GetValue - extracts the value with the specified key
and converts it to type T.
Configuration in .net Core is now built on top of POCO's or IOptions for the most part. You don't get individual keys but instead you build up settings classes. Previously you had to either build a CustomConfiguration class or you would prefix AppSettings to "group them". Not anymore! If you take the approach of using IOptions it works something like the following.
You have your appSettings.json look like the following :
{
"myConfiguration": {
"myProperty": true
}
}
You then make up a POCO that matches your configuration. Something like this :
public class MyConfiguration
{
public bool MyProperty { get; set; }
}
Then in your startup.cs you need to load your configuration into an options object. It will end up looking pretty similar to the following.
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyConfiguration>(Configuration.GetSection("myConfiguration"));
}
}
Then the DI is all set up to inject an IOptions objects. You would then inject it into a controller like so :
public class ValuesController : Controller
{
private readonly MyConfiguration _myConfiguration;
public ValuesController(IOptions<MyConfiguration> myConfiguration)
{
_myConfiguration = myConfiguration.Value;
}
}
There is other ways to do this that don't use the IOptions object and you only inject in the POCO to your controllers. Some people (including me) prefer this method. You can read more here : http://dotnetcoretutorials.com/2016/12/26/custom-configuration-sections-asp-net-core/
And of course the documentation link for the official docs are here : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration

Is there a way to customize the ObjectMapper used by Spring MVC without returning String?

I have a graph of objects that I'd like to return different views of. I don't want to use Jackson's #JsonViews to implement this. Right now, I use Jackson MixIn classes to configure which fields are shown. However, all my rest methods return a String rather than a type like BusinessCategory or Collection< BusinessCategory >. I can't figure out a way to dynamically configure the Jackson serializer based on what view I'd like of the data. Is there any feature built into Spring to configure which Jackson serializer to use on a per-function basis? I've found posts mentioning storing which fields you want in serialized in thread-local and having a filter send them and another post filtering based on Spring #Role, but nothing addressing choosing a serializer (or MixIn) on a per-function basis. Any ideas?
The key to me thinking a proposed solution is good is if the return type is an object, not String.
Here are the objects in my graph.
public class BusinessCategory implements Comparable<BusinessCategory> {
private String name;
private Set<BusinessCategory> parentCategories = new TreeSet<>();
private Set<BusinessCategory> childCategories = new TreeSet<>();
// getters, setters, compareTo, et cetera
}
I am sending these across the wire from a Spring MVC controller as JSON like so:
#RestController
#RequestMapping("/business")
public class BusinessMVC {
private Jackson2ObjectMapperBuilder mapperBuilder;
private ObjectMapper parentOnlyMapper;
#Autowired
public BusinessMVCfinal(Jackson2ObjectMapperBuilder mapperBuilder) {
this.mapperBuilder = mapperBuilder;
this.parentOnlyMapper = mapperBuilder.build();
parentOnlyMapper.registerModule(new BusinessCategoryParentsOnlyMapperModule());
}
#RequestMapping(value="/business_category/parents/{categoryName}")
#ResponseBody
public String getParentCategories(#PathVariable String categoryName) throws JsonProcessingException {
return parentOnlyMapper.writeValueAsString(
BusinessCategory.businessCategoryForName(categoryName));
}
}
I have configure the serialization in a MixIn which is in turn added to the ObjectMapper using a module.
public interface BusinessCategoryParentsOnlyMixIn {
#JsonProperty("name") String getName();
#JsonProperty("parentCategories") Set<BusinessCategory> getParentCategories();
#JsonIgnore Set<BusinessCategory> getChildCategories();
}
public class BusinessCategoryParentsOnlyMapperModule extends SimpleModule {
public BusinessCategoryParentsOnlyMapperModule() {
super("BusinessCategoryParentsOnlyMapperModule",
new Version(1, 0, 0, "SNAPSHOT", "", ""));
}
public void setupModule(SetupContext context) {
context.setMixInAnnotations(
BusinessCategory.class,
BusinessCategoryParentsOnlyMixIn.class);
}
}
My current solution works, it just doesn't feel very clean.
"categories" : [ {
"name" : "Personal Driver",
"parentCategories" : [ {
"name" : "Transportation",
"parentCategories" : [ ]
} ]
}
Oh yes, I'm using:
spring-boot 1.2.7
spring-framework: 4.1.8
jackson 2.6.3
Others listed here: http://docs.spring.io/spring-boot/docs/1.2.7.RELEASE/reference/html/appendix-dependency-versions.html
In the end, the only process that met my needs was to create a set of view objects which exposed only the fields I wanted to expose. In the grand scheme of things, it only added a small amount of seemingly unnecessary code to the project and made the flow of data easier to understand.

Setting IgnoreSerializableAttribute Globally in Json.net

I'm working on a ASP.NET WebApi (Release Candidate) project where I'm consuming several DTOs that are marked with the [Serializable] attribute. These DTOs are outside of my control so I'm not able to modify them in any way. When I return any of these from a get method the resulting JSON contains a bunch of k__BackingFields like this:
<Name>k__BackingField=Bobby
<DateCreated>k__BackingField=2012-06-19T12:35:18.6762652-05:00
Based on the searching I've done this seems like a problem with JSON.NET's IgnoreSerializableAttribute setting and to resolve my issue I just need to set it globally as the article suggests. How do I change this setting globally in a ASP.NET Web api project?
I found easy way to get rid of k__BackingField in the names.
This fragment should be somewhere in the Application_Start() in Global.asax.cs:
JsonSerializerSettings jSettings = new Newtonsoft.Json.JsonSerializerSettings();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = jSettings;
Looks like the default setting takes care of it.
Since the library does not expose a static setter for the DefaultContractResolver, I suggest you create a static wrapper over JsonConvert and it's Serialize*/Deserialize* methods (at least the ones you use).
In your static wrapper you can define a static contract resolver:
private static readonly DefaultContractResolver Resolver = new DefaultContractResolver
{
IgnoreSerializableAttribute = true
};
This you can pass to each serialization method in the JsonSerializerSettings, inside your wrapper.
Then you call your class throughout your project.
The alternative would be to get the JSON.NET source code and adjust it yourself to use that attribute by default.
For me, the following fixed the issue with circular references and k__BackingField.
In your WebApiConfig add the following to the Register() method:
JsonSerializerSettings jSettings = new Newtonsoft.Json.JsonSerializerSettings {
ContractResolver = new DefaultContractResolver {
IgnoreSerializableAttribute = true
},
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
};
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings = jSettings;
Friends, don't declare properties like this:
public String DiscretionCode { get; set; }
But, create auxiliar vars, like old....
private String discretionCode;
public String DiscretionCode
{
get { return discretionCode;}
set { discretionCode = value; }
}

Exclude property from json serialization in ApiController

I am trying to exclude properties from being serialized to JSON in web ApiControllers. I have verified the following 2 scenarios work.
I have included the following attributes on the property I wish to exclude.
[System.Web.Script.Serialization.ScriptIgnore]
[System.Xml.Serialization.XmlIgnore]
If I manually serialize my object using the JavaScriptSerializer, the property is excluded. Also, if I view the serialized XML output from the web ApiController, the property is excluded. The problem is, the serialized JSON via the web ApiController still contains the property. Is there another attribute that I can use that will exclude the property from JSON serialization?
UPDATE:
I realized all my tests were in a much more complex project and that I hadn't tried this in a an isolated environment. I did this and am still getting the same results. Here is an example of some code that is failing.
public class Person
{
public string FirstName { get; set; }
[System.Web.Script.Serialization.ScriptIgnore]
[System.Xml.Serialization.XmlIgnore]
public string LastName { get; set; }
}
public class PeopleController : ApiController
{
public IEnumerable<Person> Get()
{
return new[]
{
new Person{FirstName = "John", LastName = "Doe"},
new Person{FirstName = "Jane", LastName = "Doe"}
};
}
}
Here is the generated output.
JSON:
[
{
"FirstName" : "John",
"LastName" : "Doe"
},
{
"FirstName" : "Jane",
"LastName" : "Doe"
}
]
XML:
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfPerson xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Person>
<FirstName>John</FirstName>
</Person>
<Person>
<FirstName>Jane</FirstName>
</Person>
</ArrayOfPerson>
Be aware that JSON serialization is changing in Web API.
In the beta release, Web API used DataContractJsonSerializer to serialize JSON. However, this has changed; the latest version of Web API uses json.net by default, although you can override this and use DataContractJsonSerializer instead.
With DataContractJsonSerializer, you can use [DataContract] attributes to control the serialization. I'm stil not very familiar with json.net, so I don't know how it controls serialization.
You can use [JsonIgnore] attribute for a JSON-specific fix; or you can use [DataContract] and [DataMember] for a fix that works both with the JSON formatter, and with the (XML) DataContractSerializer.
This article provides more detailed info on the default media-type formatters:
http://www.asp.net/web-api/overview/formats-and-model-binding/json-and-xml-serialization#json_media_type_formatter
This is a little late to the game, but IgnoreDataMember is precisely what we need in your/my scenario:
[System.Runtime.Serialization.IgnoreDataMember]
public int NotSerialized { get; set; }
According to the MSDN, IgnoreDataMember came in with .NET 3.0 SP2.
JsonIgnore modifies the entire class definition. In case you want to control specific action/request, you can try this approach.
Looks like this covers what I need. It shows you you can swap out formatters. It even includes an example formatter that uses the JavaScriptSerializer which is what I need.
http://wildermuth.com/2012/2/22/WebAPI_for_the_MVC_Guy

Resources