I have a JSON file like this:
{
"date": "2012-01-05",
"rate": {
"GBP": "5.2549",
"BGN": "2.2189",
"JPY": "0.043985",
"AUD": "3.4648",
"EGP": "0.5601",
"SEK": "0.4898",
"BRL": "1.8444",
"RSD": "0.0412",
"EUR": "4.3398",
"CAD": "3.3165",
"USD": "3.3796",
"HUF": "0.013449",
"MXN": "0.2459",
"MDL": "0.2867",
"NOK": "0.5646",
"CZK": "0.1672",
"KRW": "0.002925",
"RUB": "0.1057",
"PLN": "0.9596",
"CNY": "0.5363",
"NZD": "2.6406",
"UAH": "0.4194",
"XDR": "5.1605",
"TRY": "1.7911",
"DKK": "0.5837",
"INR": "0.0638",
"CHF": "3.5629",
"XAU": "175.2918",
"AED": "0.9201",
"ZAR": "0.4114"
}
}
I need to get the values from rate as a List of Model (List<Model> )
public class Model
{
public string Currency {get; set;}
public double Value {get; set;}
}
Thanks!
It can be done in 2 steps.
Step - 1:
Deserialize the json string into a class like:
public class RootObject
{
public string date { get; set; }
public Dictionary<string, double> rate { get; set; }
}
with the following familiar code:
string json = "YOUR_JSON";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
Step - 2:
Now you can extract the Dictionary as list of Models using the following LINQ:
List<Model> models = root.rate.Select(x => new Model {Currency = x.Key, Value = x.Value}).ToList();
Related
CommentCollection
{
"_id":"5b63f0f23846b70011330889",
"CommentType":"task",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"CommentLink":null,
"EntityName":"task2",
"participants":[
ObjectId("52ffc4a5d85242602e000000"),
ObjectId("52ffc4a5d85242602e000001")
],
"Threads":[
{
"_id":"69bcef71-3695-4340-bdec-4a6e4c58c490",
"CommentType":"task",
"UserId":ObjectId("52ffc4a5d85242602e000000"),
"CommentByUserType":"Admin",
"EntityReferenceId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"Content":"fdffd",
"ProjectName":null,
"PostedDate":"2018-08-03T13:03:05.939Z",
"Active":true,
"Attachment":[
]
}
another Collection is
userCollection
{
"Id":ObjectId("52ffc4a5d85242602e000000"),
"Name":"Pms Admin",
"Email":"pms#xtrastaff.com",
"Type":"Admin",
"UserId":"6082ef25-6f9a-4874-a832-f72e0f693409",
"UserImage":"6082ef25-6f9a-4874-a832-f72e0f693409"
}
In the CommentCollection there is an array of "participants" which is storing the id's of users (from usercollection).
My requirement is join these two collections for getting user details in my asp.net core project(Linq).Participants contains list of id's
In Mongo shell you would use $lookup which can be used on arrays like in this example and your query could look like this:
db.Comment.aggregate([
{
$lookup: {
from: "user",
localField: "participants",
foreignField: "Id",
as: "participants"
}
}
])
Which simply replaces participants with array of objects from second collection:
{
"_id" : "5b63f0f23846b70011330889",
"CommentType" : "task",
"EntityReferenceId" : "6082ef25-6f9a-4874-a832-f72e0f693409",
"CommentLink" : null,
"EntityName" : "task2",
"participants" : [
{
"_id" : ObjectId("5b6e875b9d52833fbe9879c2"),
"Id" : ObjectId("52ffc4a5d85242602e000000"),
"Name" : "Pms Admin",
"Email" : "pms#xtrastaff.com",
"Type" : "Admin",
"UserId" : "6082ef25-6f9a-4874-a832-f72e0f693409",
"UserImage" : "6082ef25-6f9a-4874-a832-f72e0f693409"
}
],
"Threads" : //...
}
In C# you can express that using Lookup syntax. First option allows you to get a list of BsonDocument type which simply skips type checking:
var collection = db.GetCollection<Comment>("Comment");
List<BsonDocument> result = collection.Aggregate()
.Lookup("user", "participants", "Id", "participants")
.ToList();
The reason why you can't use regular LINQ join here is that actually you're compaing an array with a scalar value (that's what should be in equals part of join). However if you need strongly typed result instead of BsonDocuments you can use different version of Lookup method which takes types and expressions instead of strings. So you need another class for $lookup result which might be solved using inheritance:
public class Comment
{
public string _id { get; set; }
public string CommentType { get; set; }
public string EntityReferenceId { get; set; }
public string CommentLink { get; set; }
public string EntityName { get; set; }
public ObjectId[] participants { get; set; }
public Thread[] Threads { get; set; }
}
public class CommentWithUsers : Comment
{
public User[] Users { get; set; }
}
Then you can get a list of CommentWithUser:
var comments = mydb.GetCollection<Comment>("Comment");
var users = mydb.GetCollection<User>("user");
List<CommentWithUser> result = comments.Aggregate()
.Lookup<Comment, User, CommentWithUsers>(
users,
x => x.participants,
x => x.Id,
x => x.Users).ToList();
Hi i have a C# model class. This class i need to pass as json response by setting his property . one property name Product of this class have type of another product class, when there is no data for Product i am getting all inner property value as blank but instead of that i want blank json property .
For Exp My Class is
public class Profile_BO
{
public int Id { get; set; }
public string Username { get; set; }
public Product product{ get; set; }
public class Product
{
public int Id { get; set; }
public string Type { get; set; }
}
}
i am initializing this class from C# data table like below : -
Profile_BO profile_BO = new Profile_BO();
foreach (DataRow dr in result.Tables[0].Rows)
{
profile_BO.Id = Convert.ToInt32(dr[0]);
profile_BO.Username = Convert.ToString(dr[1]);
}
Product product = new Product();
foreach (DataRow dr1 in result.Tables[1].Rows)
{
product.Id = Convert.ToInt32(dr1[0]);
product.Type = Convert.ToString(dr1[1]);
}
profile_BO.product = product;
Finally when i am passing as a response to method : -
public async Task<HttpResponseMessage> GetUserInfo(Profile_Request profile_Request)
{
return request.CreateResponse(HttpStatusCode.OK, profile_BO);
}
And when calling on client side i am getting response json if data is present in table like : -
{
"Id": "1",
"Username": "abc",
"product": {
"Id": "232",
"Type": "34cvdcbv"
}
}
But when i have no data in product table i am getting below : -
{
"Id": "1",
"Username": "abc",
"product": {
"Id": 0,
"Type": ""
}
}
But if no data i want output like below : -
{
"Id": "1",
"Username": "abc",
"product": {}
}
One other question is : - Is it right way for binding response model from dataset ?
the problem you are facing, is that you are initializing an instance of Product regardless of the fact, that there might be no product at all. this results, to its properties getting initialized with default values. Int32 defaults to 0. System.String as a reference type is null.
Profile_BO profile_BO = new Profile_BO();
foreach (DataRow dr in result.Tables[0].Rows)
{
profile_BO.Id = Convert.ToInt32(dr[0]);
profile_BO.Username = Convert.ToString(dr[1]);
}
//I am assuming you only expect one row, since oyur model uses a single Product
//and no collection of products. No need for a loop then.
if(result.Tables[1].Rows.Count == 1) {
Product product = new Product();
var dr1 = result.Tables[1].Rows[0];
product.Id = Convert.ToInt32(dr1[0]);
product.Type = Convert.ToString(dr1[1]);
profile_BO.product = product;
}
This should result in the following JSON being returned:
{
"Id": "1",
"Username": "abc",
"product": null
}
EDIT: If you really must have product : {}, then you need ot change your model.
public class Profile_BO
{
public int Id { get; set; }
public string Username { get; set; }
public object product { get; set; }
}
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
Declare product as an object. Since all classes inherit from object, you can instantiate it as an object or Product, depending on your case:
if(result.Tables[1].Rows.Count == 1) {
Product product = new Product();
var dr1 = result.Tables[1].Rows[0];
product.Id = Convert.ToInt32(dr1[0]);
product.Type = Convert.ToString(dr1[1]);
profile_BO.product = product;
}
Or:
if(result.Tables[1].Rows.Count == 0) {
var product = new object();
profile_BO.product = product;
}
This will then result in:
{"Id":1,"Username":"Foo Bar","product":{}}
However I strongly advise to go with the first approach, because this will make testing and modifications easier, since you keep your strongly typed approach.
My previous answer doesn't contribute to your final question. So here is my edited solution.
A Better Solution
May be better solution is using Custom Message Handler.
A delegating handler can also skip the inner handler and directly
create the response.
Custom Message Handler:
public class NullJsonHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var updatedResponse = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = null
};
var response = await base.SendAsync(request, cancellationToken);
if (response.Content == null)
{
response.Content = new StringContent("{}");
}
else if (response.Content is ObjectContent)
{
var contents = await response.Content.ReadAsStringAsync();
if (contents.Contains("null"))
{
contents = contents.Replace("null", "{}");
}
updatedResponse.Content = new StringContent(contents,Encoding.UTF8,"application/json");
}
var tsc = new TaskCompletionSource<HttpResponseMessage>();
tsc.SetResult(updatedResponse);
return await tsc.Task;
}
}
Register the Handler:
In Global.asax file inside Application_Start() method register your Handler by adding below code.
GlobalConfiguration.Configuration.MessageHandlers.Add(new NullJsonHandler());
Now all the Asp.NET Web API Response which contains null will be replaced with empty Json body {}.
References:
1.
2.
I have the following model:
public class PersonModel {
public int Id {get; set;}
public DateTime BirthDay {get;set;}
....
}
When a make a get request OData returns a DateTimeOffset instead of a DateTime. For instance, the request '/api/rest/people/1' returns:
{
id:1,
birthDay: "12/7/2016 8:57:58 PM +00:00"
....
}
What I want? I just need to return the Date value "12/7/2016". I have tried to implement a custom ODataPayloadValue converter but OData doesn't seem to understand the 'DateTime' type.
public override object ConvertToPayloadValue(object value, IEdmTypeReference edmTypeReference)
{
/// There isn't a IEdmtypeRefere.IsDateTime
/// Thus OData always think that DateTime is equals to DateTimeOffset
if (value is DateTime)
{
///Do some stuff here.
}
return base.ConvertToPayloadValue(value, edmTypeReference);
}
The easiest way - extend your model like this:
public class PersonModel {
public int Id {get; set;}
public DateTime BirthDay {get;set;}
// add this property and you get it. You can rename it if you want
public string BirthDate {get {return this.BirthDay.ToShourtDateString(); }}
....
}
After doing some research, I found out that there is a way to achieve what I want by implementing two classes:
// A custom serializer provider to inject the AnnotatingEntitySerializer.
public class CustomODataSerializerProvider : DefaultODataSerializerProvider
{
private AnnotatingEntitySerializer _annotatingEntitySerializer;
public CustomODataSerializerProvider()
{
_annotatingEntitySerializer = new AnnotatingEntitySerializer(this);
}
public override ODataEdmTypeSerializer GetEdmTypeSerializer(IEdmTypeReference edmType)
{
if (edmType.IsEntity())
{
return _annotatingEntitySerializer;
}
return base.GetEdmTypeSerializer(edmType);
}
}
// A custom entity serializer that adds the score annotation to document entries.
public class AnnotatingEntitySerializer : ODataEntityTypeSerializer
{
public AnnotatingEntitySerializer(ODataSerializerProvider serializerProvider)
: base(serializerProvider)
{
}
public override ODataEntry CreateEntry(SelectExpandNode selectExpandNode, EntityInstanceContext entityInstanceContext)
{
ODataEntry entry = base.CreateEntry(selectExpandNode, entityInstanceContext);
PersonModel person = entityInstanceContext.EntityInstance as PersonModel;
var property = entry.Properties.Where(x => x.Name == "birthDate").FirstOrDefault();
property.Value = ((DateTimeOffset)property.Value).DateTime.ToShortDateString();
return entry;
}
}
I'm trying to allow my entities to be ordered by the Last-Modified property in their metadata, using OData query options.
I tried using a transformer as described in Converting to JSON and accessing metadata, but when I apply ODataQueryOptions to the resulting IQueryable, I get an empty array.
The model and view-model:
public class Foo
{
public int Id { get; set; }
}
public class FooViewModel
{
public string Id { get; set; }
public DateTime LastModified { get; set; }
}
The transformer:
public class Foos_WithLastModified : AbstractTransformerCreationTask<Foo>
{
public Foos_WithLastModified()
{
TransformResults = foos => from foo in foos
let metadata = MetadataFor(foo)
select new
{
Id = foo.Id.ToString(CultureInfo.InvariantCulture),
LastModified = metadata.Value<DateTime>("Last-Modified")
};
}
}
The relevant method in FooController (_session is an IAsyncDocumentSession):
public async Task<ICollection<FooViewModel>> Get(ODataQueryOptions<FooViewModel> options)
{
var settings = new ODataValidationSettings();
settings.AllowedOrderByProperties.Add("LastModified");
options.Validate(settings);
var foos = _session.Query<Foo>()
.TransformWith<Foos_WithLastModified, FooViewModel>();
var odataFoos = (IQueryable<FooViewModel>)options.ApplyTo(foos);
return await odataFoos.ToListAsync();
}
When I hit /api/Foo, the results are as expected:
[
{
"Id": "foos/456",
"LastModified": "2015-11-23T08:43:10.913662Z"
},
{
"Id": "foos/123",
"LastModified": "2015-11-23T08:50:34.0907996Z"
}
]
But when I add OData query options (/api/Foo?$orderby=LastModified), I get an empty array: [].
I also tried changing _session to an IDocumentSession and modifying Get as follows,
[EnableQuery(AllowedOrderByProperties = "LastModified")]
public IQueryable<FooViewModel> Get()
{
return _session.Query<Foo>()
.TransformWith<Foos_WithLastModified, FooViewModel>();
}
but I get the same results.
Are transformers the wrong approach? How can I sort by Last-Modified using OData query options?
I do not know how to handle the OData stuff, never tried that, but in order to query for entities, ordered by the metadata value "Last-Modified" using only RavenDB techniques you can do the following:
Create an index for your entity (in my example a Customer). In this index we add the field LastModified that's using the document's metadata value for Last-Modified.
public class Customer_ByLastModified : AbstractIndexCreationTask<Customer>
{
public class QueryModel
{
public DateTime LastModified { get; set; }
}
public Customer_ByLastModified()
{
Map = customers => from customer in customers
select new
{
LastModified = this.MetadataFor(customer).Value<DateTime>("Last-Modified")
};
}
}
The QueryModel isn't mandatory, but it makes querying via the client API easier, imo. You can then add a Transformer to be able to use the metadata value in your return model:
public class Customers_WithLastModified : AbstractTransformerCreationTask<Customer>
{
public Customers_WithLastModified()
{
TransformResults = results => from customer in results
select new CustomerViewModel
{
Id = customer.Id,
Name = customer.Name,
LastModified = MetadataFor(customer).Value<DateTime>("Last-Modified")
};
}
}
And then query it like this:
using (var session = documentStore.OpenSession())
{
var customers = session.Query<Customer_ByLastModified.QueryModel, Customer_ByLastModified>()
.OrderByDescending(x => x.LastModified)
.TransformWith<Customers_WithLastModified, CustomerViewModel>()
.ToList();
}
Hope this helps!
When using Json.NET to serialize a MVC view model to json, I have a generic object property on my view model (public object Result { get; set;}) that is getting serialized to key-value pairs instead of an actual json object. Is there a converter I can use to force it to be serialized properly?
This is what is currently being output by Json.NET:
"result": [
{
"key": "AssetId",
"value": "b8d8fb71-2553-485b-91bf-14c6c563d78b"
},
{
"key": "SearchResultType",
"value": "Assets"
},
{
"key": "Name",
"value": "abstract-q-c-1920-1920-8"
}
]
Instead, I would want it to output this:
"result": {
"AssetId": "b8d8fb71-2553-485b-91bf-14c6c563d78b",
"SearchResultType": "Assets",
"Name": "abstract-q-c-1920-1920-8"
}
EDIT:
To answer the question of how that property is getting populated, it is via a RavenDB index:
public class SiteSearchIndexTask : AbstractMultiMapIndexCreationTask<SiteSearchResult>
{
public class Result
{
public object[] Content { get; set; }
}
public override string IndexName
{
get
{
return "SiteSearch/All";
}
}
public SiteSearchIndexTask()
{
AddMap<Asset>(items => from item in items
where item.IsDeleted == false
select new
{
Id = item.Id.ToString(),
ObjectId = item.Id,
ResultType = SearchResultType.Assets,
Title = item.Name.Boost(3),
Tags = item.Tags.Select(x => x.Name).Boost(2),
Result = (object)item,
Query = string.Join(" ", item.Description)
});
AddMap<User>(items => from item in items
where item.IsDeleted == false
select new
{
Id = item.Id,
ObjectId = item.UserId,
ResultType = SearchResultType.Users,
Title = item.Username.Boost(3),
Tags = (BoostedValue) null,
Result = (object)item,
Query = string.Join(" ", item.FullName, item.Email)
});
Store(x => x.ObjectId, FieldStorage.Yes);
Store(x => x.ResultType, FieldStorage.Yes);
Store(x => x.Title, FieldStorage.Yes);
Store(x => x.Tags, FieldStorage.Yes);
Store(x => x.Result, FieldStorage.Yes);
Store(x => x.Query, FieldStorage.Yes);
}
}
Edit 2
Here are the Asset and User models (truncated for brevity, since they're just a bunch of auto properties)
public class Asset : IHasId
{
public string Id { get; set; }
public Guid AssetId
{
get
{
Guid assetId;
Guid.TryParse((Id ?? string.Empty).Replace("assets/", ""), out assetId);
return assetId;
}
set { Id = "assets/" + value; }
}
public string Name { get; set; }
public string Description { get; set; }
}
public class User : IHasId
{
public User()
{
Status = UserStatus.Active;
}
public string Id { get; set; }
public int UserId
{
get
{
int userId;
int.TryParse((Id ?? string.Empty).Replace("users/", ""), out userId);
return userId;
}
set { Id = "users/" + value; }
}
public string Username { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public UserStatus Status { get; set; }
}
Edit 3
As it turns out, when I step through the code, that object is actually of the type Raven.Abstractions.Linq.DynamicJsonObject, which contains an array of key-value pairs. So I guess this question might be more related to Raven than Json.NET. Unless of course there is a converter to go from key-value pair to json object.
It would help to see how you are querying exactly, but you should take a look at the example on querying unlike documents with a multimap index on the RavenDB site.
When I look at your index, it seems like you are trying to do way too much in terms of field storage and encapsulation of your results. It's sometimes difficult to grasp, but the mapping done in the index is not to define how the results are returned, but rather how the index is built. Unless you are doing a Reduce or TransformResults step, you are still returning the original document.
So encapsulating the document as Result = (object)item is overkill. So is having a ResultType enum. If you need to know the type of document that matched, simply use .GetType() on the object and you will quickly see if it is a User or an Asset.
Here is how I would define your search index. Note there's some differences because there were properties you showed in your index that aren't in the models you provided. (I'm assuming there is a separate entity on the backend than the models on the front end, but adjust as needed.)
public class SearchIndex : AbstractMultiMapIndexCreationTask<SearchIndex.Result>
{
public class Result
{
public object[] Content { get; set; }
public string ResultType { get; set; }
}
public SearchIndex()
{
AddMap<Asset>(items => from item in items
select new Result
{
Content = new object[] {item.Name, item.Description},
ResultType = MetadataFor(item)["Raven-Entity-Name"].ToString()
});
AddMap<User>(items => from item in items
select new Result
{
Content = new object[] { item.Username, item.FirstName, item.LastName, item.Email },
ResultType = MetadataFor(item)["Raven-Entity-Name"].ToString()
});
Index(x => x.Content, FieldIndexing.Analyzed);
}
}
And then I would query it like so:
var results = session.Advanced
.LuceneQuery<object, SearchIndex>()
.Where("ResultType:" + resultTypeName)
.AndAlso()
.Search("Content", searchTerm);
You can then simply examine your results and find that while cast as object, you can indeed do result.GetType() and see how that object is constructed. If desired, you could also put a common interface on your entities to cast to instead of object, such as the IAmSearchable shown in the Raven example.
When you finally pass your result back through MVC, it should be serialized properly, since it will be coming from the real object and not the raven DynamicJsonObject.