I have a class which contains some items. I want to serialize an instance of this class to json using the DataContractJsonSerializer :
[DataContract]
public class Policy
{
private string expiration { get; set; }
private List<List<string>> conditions { get; set; }
public Policy(){}
public Policy(string expiration, List<List<string>> conditions){
this.expiration = expiration;
this.conditions = conditions;
}
[DataMember]
public string DateExpiration
{
get{ return expiration;}
set{expiration = value;}
}
[DataMember]
public List<List<string>> Conditions
{
get{return conditions;}
set{conditions = value;}
}
}
When serialized to json it should be like this :
{
"expiration": "2011-04-20T11:54:21.032Z",
"conditions": [
["eq", "acl", "private"],
["eq", "bucket": "myas3bucket"],
["eq", "$key", "myfilename.jpg"],
["content-length-range", 0, 20971520],
["eq", "$redirect", "myredirecturl"],
]
}
I tried like this but nothing :
string expiration = "2012-12-01T12:00:00.000Z";
List<List<string>> conditions = new List<List<string>>()
{
new List<string>(){ "[ eq", "acl", "private ]" },
new List<string>(){ "[ eq", "bucket", "myas3bucket]" },
new List<string>(){ "[ eq", "$key", "myfilename.jpg ]" },
new List<string>(){ "[ content-length-range", "0", "20971520]" },
new List<string>(){ "[ eq", "$redirect", "myredirecturl]" }
};
Policy myPolicy = new Policy(expiration,conditions);
string policy = JSONHelper.Serialize<Policy>(myPolicy);
thanks
First off... your class needs a little help:
[DataContract]
public class Policy
{
public Policy(){}
public Policy(string expiration, List<List<string>> conditions){
this.DateExpiration = expiration;
this.Conditions = conditions;
}
[DataMember]
public string DateExpiration { get; set; }
[DataMember]
public List<List<string>> Conditions { get; set; }
}
Try removing the brackets in your lists:
string expiration = "2012-12-01T12:00:00.000Z";
List<List<string>> conditions = new List<List<string>>()
{
new List<string>(){ "eq", "acl", "private" },
new List<string>(){ "eq", "bucket", "myas3bucket" },
new List<string>(){ "eq", "$key", "myfilename.jpg" },
new List<string>(){ "content-length-range", "0", "20971520" },
new List<string>(){ "eq", "$redirect", "myredirecturl" }
};
Policy myPolicy = new Policy(expiration,conditions);
string policy = JSONHelper.Serialize<Policy>(myPolicy);
Related
I am trying to obtain values for Customer attributes via the Rest API. Currently, I can retrieve customer attributes, but am unable to determine which specific attribute is associated. For example, given this code (based on https://github.com/Acumatica/AcumaticaRESTAPIClientForCSharp, Endpoint = "Default", EndpointVersion = "18.200.001", Endpoint Library = Acumatica.Default_20.200.001, Acumatica version = Cloud ERP 2020 R1, Build 20.110.0017):
var customerApi = new CustomerApi(configuration);
var customers = customerApi.GetList(top: 5, expand: "Attributes", select: "Attributes/Attribute,Attributes/Value");
foreach (var cust in customers)
{
Console.WriteLine(cust.ToString());
}
Yields this output:
{
"AccountRef": {},
"Attributes": [
{
"Value": {
"value": "True"
},
"id": "8de7a85d-6d60-4235-9d35-74a9d08d1cc6",
"rowNumber": 1,
"custom": {}
},
{
"Value": {
"value": "Sample Email Body"
},
"id": "8da2a21c-2ba3-45ba-9e12-02122c626e11",
"rowNumber": 2,
"custom": {}
}, ...
What am I missing to be able to get the attribute name returned as well? Or how I am supposed to correlate the given values back to a given attribute?
The default configuration of the Customers API is returning the Attributes array as below for the customerApi.GetList(top: 5, expand: "Attributes", select: "Attributes/Attribute,Attributes/Value"); request
"Attributes": [
{
"Attribute": {
"value": "Company Revenue"
},
"Value": {
"value": "1,000,000 to 5,000,000"
},
"id": "6df69428-7157-438f-8b61-99b2d7d1a3ad",
"rowNumber": 1,
"custom": {}
},
{
"Attribute": {
"value": "Number of Employees"
},
"Value": {
"value": "1-100"
},
"id": "15c3f47f-36eb-481b-92c0-f6b2f738732f",
"rowNumber": 2,
"custom": {}
}
]
Attributes->Attribute->Value is the identifier for the Attribute which is corresponding to the Description of the Attribute record.
Your result is returned for customerApi.GetList(top: 5, expand: "Attributes", select: "Attributes/Value"); request. Please make sure that you have included the Attributes/Attribute in the select part if you are specifying it.
UPDATE
There is a small difference in naming between 18.200 and 20.200.
In 18.200 the Attribute ID is actually named Attribute
In 20.200 the Attribute ID is renamed to Attribute ID
That is why this request is working correctly for 18.200
namespace Acumatica.Default_18_200_001.Model
{
[DataContract]
public class AttributeDetail : Entity_v3
{
[DataMember(Name="Attribute", EmitDefaultValue=false)]
public StringValue Attribute { get; set; }
[DataMember(Name="RefNoteID", EmitDefaultValue=false)]
public GuidValue RefNoteID { get; set; }
[DataMember(Name="Required", EmitDefaultValue=false)]
public BooleanValue Required { get; set; }
[DataMember(Name="Value", EmitDefaultValue=false)]
public StringValue Value { get; set; }
}
}
namespace Acumatica.Default_20_200_001.Model
{
[DataContract]
public class AttributeValue : Entity_v4
{
[DataMember(Name="AttributeID", EmitDefaultValue=false)]
public StringValue AttributeID { get; set; }
[DataMember(Name="AttributeDescription", EmitDefaultValue=false)]
public StringValue AttributeDescription { get; set; }
[DataMember(Name="RefNoteID", EmitDefaultValue=false)]
public GuidValue RefNoteID { get; set; }
[DataMember(Name="Required", EmitDefaultValue=false)]
public BooleanValue Required { get; set; }
[DataMember(Name="Value", EmitDefaultValue=false)]
public StringValue Value { get; set; }
[DataMember(Name="ValueDescription", EmitDefaultValue=false)]
public StringValue ValueDescription { get; set; }
}
}
my query
/API/Json/GetJson?Desc=test1
I get all records not just the test1 records
[Route("/API/Json/GetJson", "GET")]
public class GetJson : QueryDb<JsonModel>
{
public int? Id { get; set; }
public int? RefId { get; set; }
public int? SecondRefId { get; set; }
public int? ThirdRefId { get; set; }
public int? FourthRefId { get; set; }
public string Name { get; set; }
public string JSON { get; set; }
public string JsonType { get; set; }
public string Desc { get; set; }
public int? AuditId { get; set; }
}
public class JsonModel
{
[AutoIncrement]
[PrimaryKey]
[IgnoreOnUpdate]
public int Id { get; set; }
/// <summary>
/// Other tables the this data is relevant to
/// </summary>
public int? RefId { get; set; }
public int? SecondRefId { get; set; }
public int? ThirdRefId { get; set; }
public int? FourthRefId { get; set; }
/// <summary>
/// name that is displayed to users
/// </summary>
[Required]
public string Name { get; set; }
public string JSON { get; set; }
/// <summary>
/// Tells what data type the JSON is storing
/// </summary>
[Required]
public string JsonType { get; set; }
public string Desc { get; set; }
public int AuditId { get; set; }
public DateTime AuditStamp { get; set; } = DateTime.UtcNow;
}
also my return data has extra fields. Starting at Skip
{
"id": 4,
"refId": 9,
"secondRefId": 3,
"thirdRefId": 100,
"fourthRefId": null,
"name": "test",
"json": "JSON STRING DATA",
"jsonType": "test",
"desc": "test3",
"auditId": 0,
**"skip": null,
"take": null,
"orderBy": null,
"orderByDesc": null,
"include": null,
"fields": null,
"meta": null**
},
I updated my model to nullables and its is till returing all records. My seed data and I am using SS 5.6.0
WireUpService<IntegrationService>();
using (var db = HostContext.Resolve<IDbConnectionFactory>().Open())
{
string JSON = " \"Columns\": [\r\n {\r\n \"CompanyName\": [\r\n {\r\n \"name\": \"Company Name\",\r\n \"Visible\": \"True\",\r\n \"Sort\": \"U,A,Z[Unsorted, A-Z, Z-A]\",\r\n \"Filter\": \"Test Company\"\r\n }\r\n ],\r\n \"ParentCompnay\": [\r\n {\r\n \"name\": \"Company Name\",\r\n \"Visible\": \"True\",\r\n \"Sort\": \"U,A,Z[Unsorted, A-Z, Z-A]\",\r\n \"Filter\": \"Test Company\"\r\n }\r\n ]\r\n }";
db.DropAndCreateTable<JsonModel>();
db.Insert(new JsonModel { Desc = "test",Name = "test",JsonType = "test", JSON = JSON,RefId = 10,SecondRefId = 3, AuditId = 0, AuditStamp = DateTime.Now });
db.Insert(new JsonModel { Desc = "test1", Name = "test", JsonType = "test", JSON = JSON, RefId = 10, SecondRefId = 3, AuditId = 0, AuditStamp = DateTime.Now });
db.Insert(new JsonModel { Desc = "test2", Name = "test", JsonType = "test", JSON = JSON, RefId = 5, SecondRefId = 3, AuditId = 0, AuditStamp = DateTime.Now });
db.Insert(new JsonModel { Desc = "test3", Name = "test", JsonType = "test", JSON = JSON, RefId = 9, SecondRefId = 3,ThirdRefId = 100, AuditId = 0, AuditStamp = DateTime.Now });
}
I wasn't able to reproduce this issue using the classes provided, which I've seeded with test data that matches the query and non-matching test data that you've included in your JSON response:
db.CreateTable<JsonModel>();
db.Insert(new JsonModel { RefId = 1, SecondRefId = 1, ThirdRefId = 111, Name = "test1", Desc = "test1", JsonType = "test", JSON = "TEST1"});
db.Insert(new JsonModel { RefId = 9, SecondRefId = 3, ThirdRefId = 100, Name = "test1", Desc = "test3", JsonType = "test", JSON = "JSON STRING DATA"});
There is an issue with your GetJson model where you've specified Id and AuditId as non-nullable int properties which if not specified are populated in your GetJson Request DTO as 0 (default int).
If you're going to include required value types on your GetJson AutoQuery Service you should always be providing values for them otherwise change them into int? so they're not added to the query filter when they're not specified, e.g:
public class GetJson : QueryDb<JsonModel>
{
public int? Id { get; set; }
public int? AuditId { get; set; }
}
After doing this, it works as expected where I can query the Auto Query Service using your specified query, i.e:
var url = baseUrl.CombineWith("/API/Json/GetJson").AddQueryParam("Desc", "test1");
var json = url.GetJsonFromUrl();
json.Print();
Which works as expected returning the matching result in a QueryResponse<T> DTO, i.e:
{"Offset":0,"Total":0,"Results":[{"Id":1,"RefId":1,"SecondRefId":1,"ThirdRefId":111,"Name":"test1","JSON":"TEST1","JsonType":"test","Desc":"test1","AuditId":0,"AuditStamp":"\/Date(1577190306230-0000)\/"}],"Meta":{}}
Given the following class:
public class PayrollReport
{
[UiGridColumn(Name = "fullName",Visible = false,Width = "90")]
public string FullName { get; set; }
[UiGridColumn(Name = "weekStart", CellFilter = "date")]
public DateTime WeekStart { get; set; }
}
And this custom attribute
[AttributeUsage(AttributeTargets.All)]
public class UiGridColumn : Attribute
{
public string CellFilter { get; set; }
public string DisplayName { get; set; }
public string Name { get; set; }
public bool Visible { get; set; }
public string Width { get; set; }
}
I want to create a List<UiGridColumn> for each field with only the provided values (I don't want a null for the skipped properties).
Is it possible to create a List<UiGridColumn> where each List item has only the provided values? (I fear this isn't possible, but thought I would ask) If so, how?
If not, my second preference would be a string array like this:
[{"name":"fullName","visible":false,"width":"90"},{"name":"weekStart","cellFilter":"date"}]
I would prefer to not loop through each property and attribute and argument to manually build the desired JSON string, but I haven't been able to find an easy way to do it otherwise.
public List<Object> GetUiGridColumnDef(string className)
{
Assembly assembly = typeof(DynamicReportService).Assembly;
var type = assembly.GetType(className);
var properties = type.GetProperties();
var columnDefs = new List<object>();
foreach (var property in properties)
{
var column = new Dictionary<string, Object>();
var attributes = property.CustomAttributes;
foreach (var attribute in attributes)
{
if (attribute.AttributeType.Name != typeof(UiGridColumn).Name || attribute.NamedArguments == null)
continue;
foreach (var argument in attribute.NamedArguments)
{
column.Add(argument.MemberName, argument.TypedValue.Value);
}
}
columnDefs.Add(column);
}
return columnDefs;
}
Is there a better way to do this?
If I understand your question correctly, you want to serialize the list of attributes applied to the properties on a class?
If so, you can make a helper method to do it like this:
public static string SerializeAppliedPropertyAttributes<T>(Type targetClass) where T : Attribute
{
var attributes = targetClass.GetProperties()
.SelectMany(p => p.GetCustomAttributes<T>())
.ToList();
JsonSerializerSettings settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
Formatting = Formatting.Indented
};
return JsonConvert.SerializeObject(attributes, settings);
}
Then use it like this:
string json = SerializeAppliedPropertyAttributes<UiGridColumn>(typeof(PayrollReport));
You will end up with this output, which is pretty close to what you're looking for:
[
{
"Name": "fullName",
"Visible": false,
"Width": "90",
"TypeId": "UiGridColumn, JsonTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
},
{
"CellFilter": "date",
"Name": "weekStart",
"Visible": false,
"TypeId": "UiGridColumn, JsonTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
}
]
You'll notice the TypeId property from the base Attribute class is included, and also the property names are not camel cased. To fix that, you'll need to use a custom contract resolver:
public class SuppressAttributeTypeIdResolver : CamelCasePropertyNamesContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
if (member.DeclaringType == typeof(Attribute) && member.Name == "TypeId")
{
prop.ShouldSerialize = obj => false;
}
return prop;
}
}
Add the resolver to the serialization settings in the helper method and you should be good to go:
JsonSerializerSettings settings = new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new SuppressAttributeTypeIdResolver(),
Formatting = Formatting.Indented
};
Now the output should look like this:
[
{
"name": "fullName",
"visible": false,
"width": "90"
},
{
"cellFilter": "date",
"name": "weekStart",
"visible": false
}
]
Demo fiddle: https://dotnetfiddle.net/2R5Zyi
I have the following structure in my json file
"missionName": "missionname",
"thumb_image": "pics/mission-info.png",
"uplinkPage": [
{
"RPC": {
"name": "RPC",
"rpc": "",
"args": ""
},
"EXE": {
"name": "app1",
"prog": "",
"args": ""
},
"VM": {
"name": "VM",
"name": "",
"args": ""
},
"JAR": {
"name": "JAR",
"prog": "",
"args": ""
},
"link": {
"name": "somelink",
"url": ""
}
}
],
for this I have the following class
public class EXE
{
public string name { get; set; }
public string prog { get; set; }
public string args { get; set; }
}
public class RPC
{
public string name { get; set; }
public string rpc { get; set; }
public string args { get; set; }
}
public class VM
{
public string name { get; set; }
public string args { get; set; }
}
public class JAR
{
public string name { get; set; }
public string prog { get; set; }
public string args { get; set; }
}
public class Link
{
public string name { get; set; }
public string url { get; set; }
}
public class UplinkPage
{
public VM[] vmList { get; set; }
public EXE[] exeList { get; set; }
public RPC[] rpcList { get; set; }
public JAR[] jarList { get; set; }
public Link[] linkList { get; set; }
}
public class Rootobject
{
public string missionName { get; set; }
public string thumb_image { get; set; }
public Uplinkpage[] uplinkPage { get; set; }
}
the uplinkPage section can have one or many of each of the EXE, RPC, VM .. sections. I have tried to add multiply sections like this
"EXE": {
"1": {
"name": "app1",
"data-prog": "",
"data-args": ""
},
"2": {
"name": "app2",
"data-prog": "",
"data-args": ""
}
}
when I do the deserialization as so
Rootobject page = JsonConvert.DeserializeObject<Rootobject>(File.ReadAllText("mission1.json"));
I get an object and I can read everything except if I have multiply values of the same type, I will only get one of them. If I declare sections as array
public EXE[] exeList { get; set; }
I will get the last one and if as
Dictionary<string,EXE> exeList {get; set;}
I will get the first one. I have noticed that when I use the dictionary the type of the EXE changes to EXE1.
So I was wondering what am I doing wrong? am I mission some thing here?
cheers,
es
Whenever you have a JSON property that might be duplicated, it will be easiest to represent the value of that property as JSON array, rather than multiple properties with duplicated names. I.e. instead of
{
"EXE" : {"id":1},
"RPC" : {"name":"a"},
"EXE" : {"id":2},
}
You want to do:
{
"EXE" : [{"id":1}, {"id":2}],
"RPC" : [{"name":"a"}]
}
Similarly, in the VM class, name appears multiple times, so name should be an array as well:
public class VM
{
public string [] name { get; set; }
public string args { get; set; }
}
(It's not impossible to deserialize duplicated property names, just difficult, and requires a custom converter. See How to deserialize JSON with duplicate property names in the same object. Since you control your JSON format, I'd recommend avoiding this. Using a nested object with indexed properties as you propose in your question is also an option; it is less difficult but still requires custom conversion. See How to parse this JSON using Newton Soft for nested object. But using a JSON array is easiest.)
Next, you need to tell Json.NET how to map c# property names to JSON property names when they are inconsistent. For instance, in your JSON you have a property "EXE" but in c# the property name is public EXE[] exeList { get; set; }. You can either rename the JSON properties, rename the c# properties, or make the mapping using [JsonProperty]:
public class UplinkPage
{
[JsonProperty("VM")]
public VM[] vmList { get; set; }
[JsonProperty("EXE")]
public EXE[] exeList { get; set; }
[JsonProperty("RPC")]
public RPC[] rpcList { get; set; }
[JsonProperty("JAR")]
public JAR[] jarList { get; set; }
[JsonProperty("link")]
public Link[] linkList { get; set; }
}
I notice also that your EXE object sometimes has a "data-prog" property, and sometimes just a "prog". You should standardize on one.
Thus your JSON should look like:
{
"missionName": "missionname",
"thumb_image": "pics/mission-info.png",
"uplinkPage": [
{
"RPC": [
{
"name": "RPC",
"rpc": "",
"args": ""
}
],
"EXE": [
{
"name": "app1",
"prog": "prog1",
"args": "args1"
},
{
"name": "app2",
"prog": "prog2",
"args": "args2"
}
],
"VM": [
{
"name": [
"VM",
""
],
"args": ""
}
],
"JAR": [
{
"name": "JAR",
"prog": "",
"args": ""
}
],
"link": [
{
"name": "somelink",
"url": ""
}
]
}
]
}
And your classes should look like:
public class EXE
{
public string name { get; set; }
public string prog { get; set; }
public string args { get; set; }
}
public class RPC
{
public string name { get; set; }
public string rpc { get; set; }
public string args { get; set; }
}
public class VM
{
public string [] name { get; set; }
public string args { get; set; }
}
public class JAR
{
public string name { get; set; }
public string prog { get; set; }
public string args { get; set; }
}
public class Link
{
public string name { get; set; }
public string url { get; set; }
}
public class UplinkPage
{
[JsonProperty("VM")]
public VM[] vmList { get; set; }
[JsonProperty("EXE")]
public EXE[] exeList { get; set; }
[JsonProperty("RPC")]
public RPC[] rpcList { get; set; }
[JsonProperty("JAR")]
public JAR[] jarList { get; set; }
[JsonProperty("link")]
public Link[] linkList { get; set; }
}
public class Rootobject
{
public string missionName { get; set; }
public string thumb_image { get; set; }
public UplinkPage[] uplinkPage { get; set; }
}
prototype fiddle.
I want to ignore some properties from my Object during run time. The properties are decorated with data member attribute (Without data member attribute excludepropertyreferencces is working fine). Can you please provide some insight? Thanks
Question : HOW TO EXCLUDE PROPERTIES AT RUN TIME, IF THEY ARE DECORATE WITH DATAMEMBER ATTRIBUTE ?
ServiceStack , ExcludePropertyReferences
var model = new Model {
FirstName = "First Name",
LastName = "Last Name",
Children = new List<ModelChild>{
new ModelChild { ChildFirstName = "ChildFirstName 1", ChildLastName = "ChildLastName 1" },
new ModelChild { ChildFirstName = "ChildFirstName 2", ChildLastName = "ChildLastName 2" }
}
};
var model1 = new Model1 {
FirstName = "First Name",
LastName = "Last Name",
Children = new List<Model1Child>{
new Model1Child { ChildFirstName = "ChildFirstName 1", ChildLastName = "ChildLastName 1" },
new Model1Child { ChildFirstName = "ChildFirstName 2", ChildLastName = "ChildLastName 2" }
}
};
Console.WriteLine("Properties won't get ignored because the Model is decorated with Serialization Attributes");
using(MemoryStream stream = new MemoryStream())
using (var jsConfig = JsConfig.BeginScope()) {
jsConfig.ExcludeTypeInfo = true;
jsConfig.ExcludePropertyReferences = new [] { "Model.LastName", "ModelChild.ChildLastName" }.ToArray();
JsonSerializer.SerializeToStream(model, model.GetType(), stream);
LINQPad.Extensions.Dump(System.Text.Encoding.Default.GetString(stream.ToArray()));
}
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("Properties will get ignored because the Model is not decorated with Serialization Attributes");
using(MemoryStream stream = new MemoryStream())
using (var jsConfig = JsConfig.BeginScope()) {
jsConfig.ExcludeTypeInfo = true;
jsConfig.ExcludePropertyReferences = new [] { "Model1.LastName", "Model1Child.ChildLastName" }.ToArray();
JsonSerializer.SerializeToStream(model1, model1.GetType(), stream);
LINQPad.Extensions.Dump(System.Text.Encoding.Default.GetString(stream.ToArray()));
}
// Define other methods and classes here
[DataContract()]
public class Model {
[DataMember(Name = "first_name",EmitDefaultValue = false )]
public string FirstName { get; set; }
[DataMember(Name = "last_name")]
public string LastName { get; set; }
[DataMember(Name = "collections")]
public List<ModelChild> Children { get; set; }
}
[DataContract()]
public class ModelChild {
[DataMember(Name = "child_first_name")]
public string ChildFirstName { get; set; }
[DataMember(Name = "child_last_name")]
public string ChildLastName { get; set; }
}
public class Model1 {
public string FirstName { get; set; }
public string LastName { get; set; }
public List<Model1Child> Children { get; set; }
}
public class Model1Child {
public string ChildFirstName { get; set; }
public string ChildLastName { get; set; }
}