Given this request DTO
public class CreateRecordRequest {
public Dictionary<string, object> Record { get; set; }
}
when I call the service passing this JSON
{
"Record": {
"File": {
"name": "DSC_3493_4_5.jpg",
"extension": ".jpg",
"size": 596002,
"rawFile": {}
},
"Notes": "",
"Type": ""
}
}
File has the deserialized value "{". Since ServiceStack has no way of knowing which object File maps to, I'm curious why it doesn't deserialize it as a dictionary ("{" is inexplicable). What is the easiest way to customize deserialization of a single value like this? I'm working with Kendo's upload control and this is the JSON it submits.
Because it's an object the Serializer doesn't know what type to dehydrate this into, you can force it to use a Dictionary<string,string> with:
JsConfig.ConvertObjectTypesIntoStringDictionary = true;
Related
I want to take the raw JSON body from an HTTP post and write it directly into my CosmosDB.
Let's say the data looks like this:
{
"id": "123456",
"storeName": "City Bar and Grille",
"invoiceTotal": 65
}
However, the documentsOut.AddAsync command uses a format like this:
wait documentsOut.AddAsync(new
{
// create a random ID
id = System.Guid.NewGuid().ToString(),
body = sourceJson
});
And then I end up with a document that looks like this:
{
"id": "0e99d3ab-1956-4c0a-8ec1-99de5c987555",
"body": {
"id": "123456",
"storeName": "City Bar and Grille",
"invoiceTotal": 65
}
}
What I really want is to end up with this:
{
"id": "123456",
"storeName": "City Bar and Grille",
"invoiceTotal": 65
}
I'd like to drop the id = System.Guid.NewGuid().ToString() completely (which should not be hard).
How can I pass the raw JSON through without needing to add it to some parent node (such as body)?
Just to formalize my comment as an answer: You're specifically creating the body property of the Cosmos DB document you're creating:
wait documentsOut.AddAsync(new
{
// create a random ID
id = System.Guid.NewGuid().ToString(),
body = sourceJson
});
At the same time, you're ignoring the incoming ID. Since you wanted to preserve that ID, You can copy the ID over (as long as it remains unique within the partition) instead of generating a new GUID, and also grab individual properties from sourceJson to avoid the nested body element:
wait documentsOut.AddAsync(new
{
id = sourceJson.id,
storeName = sourceJson.storeName,
invoiceTotal = sourceJson.invoiceTotal
});
Using a similar example as shared by you in the question.
We can create a Model class with properties which want to store in database.
public class StoreDetails : TableEntity
{
[JsonProperty("id")]
public string Id { get; set; }
[JsonProperty("storeName")]
public string StoreName { get; set; }
[JsonProperty("invoiceTotal")]
public string InvoiceTotal { get; set; }
}
Then can create object of model and pass that object to AddAsync()method. As shown in below screenshot.
var item = new StoreDetails
{
Id = Guid.NewGuid().ToString(),
StoreName = data.StoreName,
InvoiceTotal = data.InvoiceTotal
};
await storeDetailOut.AddAsync(item);
and when we triggered post API, got response as shown below.
As finally we can see the same record is stored in CosmosDB.
I have a class as below and try to use EF core framework to store the below model to Cosmos DB. But the the JSON is stored as
{
"id": "e6b75f1f-0cc2-488c-9074-62e7e85c727a",
"Type ": "testType",
"TagName ": "TagName",
"DictionaryList ": {}
}
public class TestDictionary
{
public Guid Id { get; set; }
public string Type { get; set; }
public string TagName { get; set; }
public Dictionary<string,object> DictionaryList { get; set; }
}
Problem is that the DictionaryList property doesn't save the data that is passed from api. It always stores as empty object instead of data passed through dictionary
I want my Jason to be stored in cosmos as below
{
"id": "e6b75f1f-0cc2-488c-9074-62e7e85c727a",
"Type ": "testType",
"TagName ": "TagName",
"DictionaryList ": {
“Account number”: “123456”,
“Check date”: “11/20/2020”
}
}
Only collections of primitive types are supported by EF Core 6, So DictionaryList needs to be typed as Dictionary<string,string>
Dictionary<string,object> is recognized as a nested entity type, you could use it, but you'd need to explicitly configure all the keys that it can contain:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<TestDictionary>()
.OwnsOne(t => t.DictionaryList, c =>
{
c.Property<string>("Type");
c.Property<string>("TagName");
});
}
I later realized that I could using the cosmos sdk insert Dictionary<stringm, object> without any problem, and that Ef core does expose the underlying cosmos client.
So I solved the issue by access the underlying cosmos client, and use that to insert my dictionary.
var cosmosClient = dbContext.Database.GetCosmosClient();
var container = cosmosClient.GetContainer( "db", "container");
await container.CreateItemAsync(new Dictionary<string,object>);
works like a charm.
remember the Id has to be id or atleast serializer has to be told that Id has to be interpreted id
I might seem lost, but I feel that my code is correct but it does not work. I implemented my own PXStringListAttribute, thus removing the base Attribute of existing PXStringListAttribute, but it seems not working.
[PXRemoveBaseAttribute(typeof(ActivityStatusListAttribute))]
[CustomStatusApproval]
[PXUIField(DisplayName="test")]
protected virtual void CRActivity_TimeActivity_ApprovalStatus_CacheAttached(PXCache cache)
{
}
The field you are trying to customize is PMTimeActivity.ApprovalStatus so your cache attached method name is not pointing to the correct reference to override the attributes.
Viewing the properties of the field you can see the dac and field you should use:
This results in the cache attached method name of PMTimeActivity_ApprovalStatus_CacheAttached (using the naming convention of DataClass_DataField_CacheAttached)
Here is a working example:
public class CRActivityMaintExtension : PXGraphExtension<PX.Objects.EP.CRActivityMaint>
{
[PXRemoveBaseAttribute(typeof(PX.Objects.CR.ActivityStatusListAttribute))]
[PXStringList(new[]
{
PX.Objects.CR.ActivityStatusListAttribute.Draft,
PX.Objects.CR.ActivityStatusListAttribute.Open,
PX.Objects.CR.ActivityStatusListAttribute.InProcess,
PX.Objects.CR.ActivityStatusListAttribute.Completed,
PX.Objects.CR.ActivityStatusListAttribute.Approved,
PX.Objects.CR.ActivityStatusListAttribute.Rejected,
PX.Objects.CR.ActivityStatusListAttribute.Canceled,
PX.Objects.CR.ActivityStatusListAttribute.PendingApproval,
PX.Objects.CR.ActivityStatusListAttribute.Released,
"T1",
"T2"
},
new[]
{
PX.Objects.EP.Messages.Draft,
PX.Objects.EP.Messages.Open,
PX.Objects.EP.Messages.InProcess,
PX.Objects.EP.Messages.Completed,
PX.Objects.EP.Messages.Approved,
PX.Objects.EP.Messages.Rejected,
PX.Objects.EP.Messages.Canceled,
PX.Objects.EP.Messages.Balanced,
PX.Objects.EP.Messages.Released,
"TEST1",
"TEST2"
})]
[PXUIField(DisplayName = "Some Test")]
protected virtual void PMTimeActivity_ApprovalStatus_CacheAttached(PXCache cache)
{
}
}
I would like to post a JSON object to my service stack service and use a dynamic property in the request DTO. All approaches I have tried so far leave the object being a NULL value.
The javascript code I use:
$.getJSON(
"/api/json/reply/Hello",
{
Name: "Murphy",
Laws: {
SomeProp: "A list of my laws",
SomeArr: [
{ Title: "First law" },
{ Title: "Second law" },
{ Title: "Third law" }
]
}
},
function(data) {
alert(data.result);
}
);
The DTO to receive the request:
public class Hello
{
public string Name { get; set; }
public dynamic Laws { get; set; }
}
I also tried to use an object and JsonObject instead of dynamic in the DTO.
To be complete, here's the service too:
public class HelloService : Service
{
public object Any(Hello request)
{
return new HelloResponse { Result = "Hello, " + request.Name };
}
}
Murphy comes through in the Name property without any problems, but the Laws property remains NULL.
In the end, I want to somehow iterate (using reflection?) over the Laws property and get all the contained properties and values.
I cannot use a typed DTO here, because I don't know the JSON of the Laws property at development time (and it can change quite frequently).
Thanks for any help!
The .NET 3.5 library builds of ServiceStack on NuGet doesn't have native support for the .NET 4.0+ dynamic type. You can pass JSON into a string property and dynamically parse it on the server:
public object Any(Hello request)
{
var laws = JsonObject.Parse(request.Laws);
laws["SomeProp"] //
laws.ArrayObjects("SomeArr") //
}
Otherwise You can use Dictionary<string,string> or if you specify in your AppHost:
JsConfig.ConvertObjectTypesIntoStringDictionary = true;
You can use object which will treat objects like a string dictionary.
Otherwise dynamic shouldn't be on the DTO as it's meaningless as to what the service expects. You could just add it to the QueryString. You can use the JSV Format to specify complex object graphs in the QueryString, e.g:
/hello?laws={SomeProp:A list of my laws,SomeArr:[{Title:First Law}]}
Note: the spaces above gets encoded with %20 on the wire.
Which you can access in your services with:
public object Any(Hello request)
{
var laws = base.QueryString["laws"].FromJsv<SomeTypeMatchingJsvSent>();
}
I was using Json.Net to serialize dictionaries of type Dictionary, and when I added integers or booleans to the dictionary, when deserializing I would get integers and booleans back.
Now I was trying to change my code to use ServiceStack.Text instead because of a problem in other part of the code with the serialization of dates, but now I get the booleans an integers as strings after deserialization. Is there any way to have the same behaviour as Json.Net?
Here's the code to reproduce it: https://gist.github.com/1608951
Test_JsonNet passes, but both Test_ServiceStack_Text_TypeSerializer and Test_ServiceStack_Text_JsonSerializer fail
For untyped dynamic payloads it's better to use the JSON Utils in ServiceStack.Common which lets you parse dynamic payloads whilst preserving their type, e.g:
var obj = JSON.parse(json);
if (obj is new Dictionary<string, object> jsonObj)
{
int b = (int)jsonObj["b"];
bool c = (bool)jsonObj["c"];
}
It's a purposeful design decision in ServiceStack.Text JSON Serializer that no type information is emitted for primitive value types which is why the values are left as strings.
You either have to deserialize into a strong typed POCO that holds the type info:
public class MixType
{
public string a { get; set; }
public int b { get; set; }
public bool c{ get; set; }
}
var mixedMap = new Dictionary<string, object> {
{ "a", "text" },
{ "b", 32 },
{ "c", false },
};
var json = JsonSerializer.SerializeToString(mixedMap);
Console.WriteLine("JSON:\n" + json);
var mixedType = json.FromJson<MixType>();
Assert.AreEqual("text", mixedType.a);
Assert.AreEqual(32, mixedType.b);
Assert.AreEqual(false, mixedType.c);
Or deserialize into a Dictionary<string,string> and parse into specific types yourself.
Or deserialize using ServiceStack's dynamic API. See ServiceStack's Dynamic JSON Test folder for examples on how to do this.
You can now do this:
JsConfig.TryToParsePrimitiveTypeValues = true;
Which will make the JSON deserialiser try to determine what the types should be, and you will get your integers back as integers, booleans back as booleans, etc.