So i have a REST API that works with a JSON based on OData exchange. In a OData type there is a ID property i want to read so i can do some checking on it. However when i want to write back to the webserver the ID property must not be present in the response JSON string. So it has to be a write-only property, but simply changing a property to write-only prevents be to check what the value is of that property.
For example, i create a new product:
Public Class Product
Public property ID as integer
Public property Title as string
End class
GET response:
{
"ID" = 1,
"Title" = "Cool product!"
}
POST Wrong:
{
"ID" = 1, <---- ignore this value
"Title" = "Cool product! Changed!"
}
POST Should be:
{
"Title" = "Cool product! Changed!"
}
The webserver uses OData
Using the attribute JsonIgnoredoesn't fix it because the value of the REST response isn't serialized then.
This is for WPF and not ASP.Net
JSON.NET supports conditional property serialization using a method that returns a bool and has the same name as the property with a ShouldSerialize prefix:
public class Product
{
public int Id { get; set; }
public string Title { get; set; }
public bool ShouldSerializeId() => false;
}
Related
I have a class (MyClass), in which there is a reference type field (let's call MyNestedClass).
I'm using Newtonsoft, and I would like to serialize this class, including this field. But instead of "opening a new block" in the Json (a Json object), I would like this field to remain in the same level as the other (primitive) fields of this class. It's guaranteed, that the MyNestedClass contains only one field.
public class MyClass
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("nested_prop", Required = Required.Always)]
public MyNestedClass NestedProp { get; set; }
...
}
public class MyNestedClass
{
public string Address { get; set; }
}
output (with some values):
{
"name" : "foo_val",
"nested_prop" : {
"Address" : "bar_val" // it gets its name (Address) by default (based on the prop name)
}
}
what I want:
{
"name" : "foo_val",
"address" : "bar_val"
}
(of course I have my reasons why do I need a separate class (MyNestedClass) instead of putting that string (Address) into the MyClass class)
I tried to create a converter (CustomCreationConverter) with overriding the WriteJson method, but it never gets called during the serialization process...
================================================
Edit:
I used CustomCreationConverter because I have already overridden its Create method (my prop type in my real problem is an interface, and not the concrete class)
As #dbc hinted, WriteJson hasn't been called because in CustomCreationConverter the CanWrite method is overriden with return false. Overriding also this method in my custom converer solves this problem.
I am converting my newtonsoft implementation to new JSON library in .net core 3.0. I have the following code
public static bool IsValidJson(string json)
{
try
{
JObject.Parse(json);
return true;
}
catch (Exception ex)
{
Logger.ErrorFormat("Invalid Json Received {0}", json);
Logger.Fatal(ex.Message);
return false;
}
}
I am not able to find any equivalent for JObject.Parse(json);
Also what will be the attribute JsonProperty equivalent
public class ResponseJson
{
[JsonProperty(PropertyName = "status")]
public bool Status { get; set; }
[JsonProperty(PropertyName = "message")]
public string Message { get; set; }
[JsonProperty(PropertyName = "Log_id")]
public string LogId { get; set; }
[JsonProperty(PropertyName = "Log_status")]
public string LogStatus { get; set; }
public string FailureReason { get; set; }
}
One more thing i will be looking for the equivalent of Formating.None.
You are asking a few questions here:
I am not able to find any equivalent for JObject.Parse(json);
You can use JsonDocument to parse and examine any JSON, starting with its RootElement. The root element is of type JsonElement which represents any JSON value (primitive or not) and corresponds to Newtonsoft's JToken.
But do take note of this documentation remark:
This class utilizes resources from pooled memory to minimize the impact of the garbage collector (GC) in high-usage scenarios. Failure to properly dispose this object will result in the memory not being returned to the pool, which will increase GC impact across various parts of the framework.
When you need to use a JsonElement outside the lifetime of its document, you must clone it:
Gets a JsonElement that can be safely stored beyond the lifetime of the original JsonDocument.
Also note that JsonDocument is currently read-only and does not provide an API for creating or modifying JSON. There is an open issue Issue #39922: Writable Json DOM tracking this.
An example of use is as follows:
//https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#using-declarations
using var doc = JsonDocument.Parse(json);
//Print the property names.
var names = doc.RootElement.EnumerateObject().Select(p => p.Name);
Console.WriteLine("Property names: {0}", string.Join(",", names)); // Property names: status,message,Log_id,Log_status,FailureReason
//Re-serialize with indentation.
using var ms = new MemoryStream();
using (var writer = new Utf8JsonWriter(ms, new JsonWriterOptions { Indented = true }))
{
doc.WriteTo(writer);
}
var json2 = Encoding.UTF8.GetString(ms.GetBuffer(), 0, checked((int)ms.Length));
Console.WriteLine(json2);
Also what will be the attribute JsonProperty equivalent?
Attributes that can control JsonSerializer are placed in the System.Text.Json.Serialization namespace and inherit from an abstract base class JsonAttribute. Unlike JsonProperty, there is no omnibus attribute that can control all aspects of property serialization. Instead there are specific attributes to control specific aspects.
As of .NET Core 3 these include:
[JsonPropertyNameAttribute(string)]:
Specifies the property name that is present in the JSON when serializing and deserializing. This overrides any naming policy specified by JsonNamingPolicy.
This is attribute you want to use to control the serialized names of your ResponseJson class:
public class ResponseJson
{
[JsonPropertyName("status")]
public bool Status { get; set; }
[JsonPropertyName("message")]
public string Message { get; set; }
[JsonPropertyName("Log_id")]
public string LogId { get; set; }
[JsonPropertyName("Log_status")]
public string LogStatus { get; set; }
public string FailureReason { get; set; }
}
[JsonConverterAttribute(Type)]:
When placed on a type, the specified converter will be used unless a compatible converter is added to the JsonSerializerOptions.Converters collection or there is another JsonConverterAttribute on a property of the same type.
Note that the documented priority of converters -- Attribute on property, then the Converters collection in options, then the Attribute on type -- differs from the documented order for Newtonsoft converters, which is the JsonConverter defined by attribute on a member, then the JsonConverter defined by an attribute on a class, and finally any converters passed to the JsonSerializer.
[JsonExtensionDataAttribute] - corresponds to Newtonsoft's [JsonExtensionData].
[JsonIgnoreAttribute] - corresponds to Newtonsoft's [JsonIgnore].
When writing JSON via Utf8JsonWriter, indentation can be controlled by setting JsonWriterOptions.Indented to true or false.
When serializing to JSON via JsonSerializer.Serialize, indentation can be controlled by setting JsonSerializerOptions.WriteIndented to true or false.
Demo fiddle here showing serialization with JsonSerializer and parsing with JsonDocument.
This link should get you going, snippets of which I copied below.
https://devblogs.microsoft.com/dotnet/try-the-new-system-text-json-apis/
WeatherForecast Deserialize(string json)
{
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true
};
return JsonSerializer.Parse<WeatherForecast>(json, options);
}
class WeatherForecast {
public DateTimeOffset Date { get; set; }
// Always in Celsius.
[JsonPropertyName("temp")]
public int TemperatureC { get; set; }
public string Summary { get; set; }
// Don't serialize this property.
[JsonIgnore]
public bool IsHot => TemperatureC >= 30;
}
I have extracted the below json from ETH. So this is a valid json.
"networks": {
"18": {
"address": "0x478a2763d239b60206006437f5154dad59fef909"
}
}
Trying to parse using:
dynamic Obj = JsonConvert.DeserializeObject(".... json string .....");
Obj.Networks.18.address; // Error
Obj.SelectToken("networks.18.address"); // Null
I can't even compile because a label name cannot start with number.
May I know what is the correct way to access the address?
You can create a model that represents the data
public class RootObject {
public Dictionary<string, network> networks { get; set; }
}
public class network {
public string address { get; set; }
}
And use that to access the desired information
var data = JsonConvert.DeserializeObject<RootObject>(".... json string .....");
var address = data.networks["18"].address;
because of the syntax conflict, string Dictionary was used as a workaround to be able to access the key value pair.
I have the following JSON:
[{
"theme-my-login":
{
"latest_version":"6.4.7",
"last_updated":"2017-01-06T18:14:00.000Z",
"popular":true,
"vulnerabilities":
[
{
"id":6043,
"title":"Theme My Login 6.3.9 - Local File Inclusion",
"created_at":"2014-08-01T10:58:35.000Z",
"updated_at":"2015-05-15T13:47:24.000Z",
"published_date":null,
"references":
{
"url":["http://packetstormsecurity.com/files/127302/","http://seclists.org/fulldisclosure/2014/Jun/172","http://www.securityfocus.com/bid/68254/","https://security.dxw.com/advisories/lfi-in-theme-my-login/"]
},
"vuln_type":"LFI",
"fixed_in":"6.3.10"
}
]
}
},{
"other-item":
{
"latest_version":"6.4.7",
"last_updated":"2017-01-06T18:14:00.000Z",
"popular":true,
"vulnerabilities":
[
{
"id":6043,
"title":"Theme My Login 6.3.9 - Local File Inclusion",
"created_at":"2014-08-01T10:58:35.000Z",
"updated_at":"2015-05-15T13:47:24.000Z",
"published_date":null,
"references":
{
"url":["http://packetstormsecurity.com/files/127302/","http://seclists.org/fulldisclosure/2014/Jun/172","http://www.securityfocus.com/bid/68254/","https://security.dxw.com/advisories/lfi-in-theme-my-login/"]
},
"vuln_type":"LFI",
"fixed_in":"6.3.10"
}
]
}
}]
json2csharp says the object model should look like this, but that's clearly not correct
public class References
{
public List<string> url { get; set; }
}
public class Vulnerability
{
public int id { get; set; }
public string title { get; set; }
public DateTime created_at { get; set; }
public DateTime updated_at { get; set; }
public object published_date { get; set; }
public References references { get; set; }
public string vuln_type { get; set; }
public string fixed_in { get; set; }
}
public class ThemeMyLogin
{
public string latest_version { get; set; }
public DateTime last_updated { get; set; }
public bool popular { get; set; }
public List<Vulnerability> vulnerabilities { get; set; }
}
public class RootObject
{
public ThemeMyLogin __invalid_name__theme-my-login { get; set; }
}
that I am trying to deserialise into c# classes using Json.NET, but as the top level item doesn't have a traditional name:value pair (the name effectively is "theme-my-login" and the value is the object), it's not deserialising.
Any pointers on how I can get this to deserialise? Do I need to use a custom deserialiser?
The reason I cannot use a dictionary as suggested in How can I parse a JSON string that would cause illegal C# identifiers? is that I need the value "theme-my-login" as one of the values in my model as it defines the object. I have added a second item into the json as this will be a list of items. I previously only included one to show the item structure.
You need to deserialize to List<Dictionary<string, ThemeMyLogin>> like so:
var root = JsonConvert.DeserializeObject<List<Dictionary<string, ThemeMyLogin>>>(json);
The code-generation site http://json2csharp.com/ has some limitations of which you need to be aware:
The JSON standard allows for two types of container:
The array, which is an ordered collection of values. An array begins with [ (left bracket) and ends with ] (right bracket). Values are separated by , (comma).
The object, which is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace).
If your root container is an array, http://json2csharp.com/ will auto-generate a RootObject model to deserialize each object in the array. To actually deserialize the entire array you need to deserialize to a collection of root objects such as a List<RootObject>. See Serialization Guide: IEnumerable, Lists, and Arrays.
When a JSON property corresponds to an invalid c# identifier, http://json2csharp.com/ will "helpfully" add a property to the containing type that looks like this:
public PropertyType __invalid_name__my-invalid-identifier { get; set; }
Of course this will not compile, so you need to notice any __invalid_name properties and manually fix the generated code. Options for doing this include those covered in How can I parse a JSON string that would cause illegal C# identifiers? and elsewhere:
If the property name is fixed and known in advance, rename the c# property to something valid consistent with your coding conventions and mark it with [JsonProperty("my-invalid-identifier")]. (From the answer by ken2k).
If the containing type consists entirely of variable property names with a fixed schema for their values corresponding to some type T, replace the containing type with a Dictionary<string, T>. (From the answer by L.B.)
If the containing object has a mixture of fixed and variable properties, see Deserialize json with known and unknown fields or How to deserialize a child object with dynamic (numeric) key names?.
You seem to have encountered both limitations. Working sample .Net fiddle.
I'm trying to mimick a c# class definition taken in on the body of a webapi call in powershell so I can call a rest method with that object represented as json. An example of the structure of json I'm trying to achieve would be:
'{
"Name" : "Test" ,
"Items" : [{"Key" : "Test", "Value" : "t2"},{"Key" : "Test2", "Value" : "t3"}]
}'
This json works when set to the body of the rest call in postman, if I call the powershell invokerest-method with it all fields are simply null on the webapi end.
Here is my current attempt at this in powershell, note, Items is effectively a list of key value pair objects. Using the below code I just get 1 item in items, with a null name and value.
$spec = #{
Name = 'Test'
Items = ,{Name = 'Test', Value = 't2'}, {Name = 'Test2', Value = 't3'}
}
invoke-restmethod $someurl -Method:POST -body $spec | Write-Host
I'm fairly sure I'm missing something important with the array declaration for items on the powershell object.
Here is the rest method:
[HttpPost]
[Route("addorupdateitem")]
public void Add([FromBody] ItemConfigurationDto itemconfig)
{
var serializeObject = Newtonsoft.Json.JsonConvert.SerializeObject(context);
Debug.WriteLine(serializeObject);
}
and the objects
public class ItemConfigurationDto
{
public string Name { get; set; }
public List<Item> Items { get; set; } = new List<Item>();
}
public class Item
{
public string Name { get; set; }
public string Value { get; set; }
}
Fixed the issue. I had to set -ContentType 'application/json' and convert the object to json before adding it to the body.