JSON Output
{
"balance":100.0,
"trc10": [{
"TR7NHqjeK": "10000000",
"KQxGTCi8": "20000000"
}],
"trc20": [{
"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t": "10000000",
"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjL56": "40000000"
}]
}
public class Root
{
public double balance { get; set; }
public List<dynamic> trc10 { get; set; }
public List<dynamic> trc20 { get; set; }
}
The code to transform the JSON would look something like this:
c# code
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
Response.Write((myDeserializedClass.balance).ToString());
myDeserializedClass.balance output - 100.0
how to gat value of trc10,trc20 List item with select and where query
like myDeserializedClass.trc20.Where(x => x.TR7NHqjeKQxGTCi8q8ZY4pL8otSsssj6t == "TR7NHqjeKQxGTCi8q8ZY4pL8otSsssj6t")
Related
I am trying to create a custom resource which points to a lambda function and then invoke it to generate random Priority or my ELB Listener.
Code for Lambda function is as follows.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace aws_listenser_rule_priority_generator {
public class Function {
public async Task<int> FunctionHandler(FunctionParams input, ILambdaContext context) {
AmazonElasticLoadBalancingV2Client elbV2Client = new AmazonElasticLoadBalancingV2Client(RegionEndpoint.EUWest1);
var describeRulesResponse = await elbV2Client.DescribeRulesAsync(new DescribeRulesRequest {
ListenerArn = input.ListenerArn
});
var priority = 0;
var random = new Random();
do {
priority = random.Next(1, 50000);
}
while(describeRulesResponse.Rules.Exists(r => r.Priority == priority.ToString()));
return priority;
}
}
public class FunctionParams {
public string ListenerArn { get; set; }
}
}
I have tested this lambda on AWS console with the following parameters and it returns successfully.
{
"ListenerArn": "arn:aws:elasticloadbalancing:eu-west-1:706137030892:listener/app/Cumulus/dfcf28e0393cbf77/cdfe928b0285d5f0"
}
But as soon as I try to use this with Cloud Formation. The Custom Resource is stuck at creation in progress.
Resources:
ListenerPriority:
Type: Custom::Number
Properties:
ServiceToken: "arn:aws:lambda:eu-west-1:706137030892:function:GenerateListenerPriority"
ListenerArn: "arn:aws:elasticloadbalancing:eu-west-1:706137030892:listener/app/Cumulus/dfcf28e0393cbf77/cdfe928b0285d5f0"
The issue with the previous approach was the data format. When we are working with the Custom Resources, Cloud Formation sends a request in a specified format and response is expected asynchronously at a specified response URL.
I had to make following updates to the code:
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace aws_listenser_rule_priority_generator
{
public class Function
{
public async Task FunctionHandler(CustomResourceRequest<CustomResourceRequestProperties> crfRequest, ILambdaContext context)
{
var jsonResponse = string.Empty;
if(crfRequest.RequestType.ToUpperInvariant() != "CREATE") {
jsonResponse = JsonSerializer.Serialize(new CustomResourceResponse<object> {
Status = "SUCCESS",
Reason = string.Empty,
PhysicalResourceId = Guid.NewGuid().ToString(),
StackId = crfRequest.StackId,
RequestId = crfRequest.RequestId,
LogicalResourceId = crfRequest.LogicalResourceId,
Data = new {
Dummy = "Dummy"
}
});
}
else {
AmazonElasticLoadBalancingV2Client elbV2Client = new AmazonElasticLoadBalancingV2Client(RegionEndpoint.EUWest1);
var describeRulesResponse = await elbV2Client.DescribeRulesAsync(new DescribeRulesRequest {
ListenerArn = crfRequest.ResourceProperties.ListenerArn
});
var priority = 0;
var random = new Random();
do {
priority = random.Next(1, 50000);
}
while(describeRulesResponse.Rules.Exists(r => r.Priority == priority.ToString()));
jsonResponse = JsonSerializer.Serialize(new CustomResourceResponse<object> {
Status = "SUCCESS",
Reason = string.Empty,
PhysicalResourceId = Guid.NewGuid().ToString(),
StackId = crfRequest.StackId,
RequestId = crfRequest.RequestId,
LogicalResourceId = crfRequest.LogicalResourceId,
Data = new {
Priority = priority
}
});
}
var byteArray = Encoding.UTF8.GetBytes(jsonResponse);
var webRequest = WebRequest.Create(crfRequest.ResponseURL) as HttpWebRequest;
webRequest.Method = "PUT";
webRequest.ContentType = string.Empty;
webRequest.ContentLength = byteArray.Length;
using(Stream datastream = webRequest.GetRequestStream()) {
await datastream.WriteAsync(byteArray, 0, byteArray.Length);
}
await webRequest.GetResponseAsync();
}
}
public class CustomResourceRequest<T> where T : ICustomResourceRequestProperties {
public string RequestType { get; set; }
public string ResponseURL { get; set; }
public string StackId { get; set; }
public string RequestId { get; set; }
public string ResourceType { get; set; }
public string LogicalResourceId{ get; set; }
public string PhysicalResourceId { get; set; }
public T ResourceProperties { get; set; }
public T OldResourceProperties { get; set; }
}
public class CustomResourceResponse<T> {
public string Status { get; set; }
public string Reason { get; set; }
public string PhysicalResourceId { get; set; }
public string StackId { get; set; }
public string RequestId { get; set; }
public string LogicalResourceId { get; set; }
public bool NoEcho { get; set; }
public T Data { get; set; }
}
public interface ICustomResourceRequestProperties {
public string ServiceToken { get; set; }
}
public class CustomResourceRequestProperties : ICustomResourceRequestProperties
{
string ICustomResourceRequestProperties.ServiceToken { get; set; }
public string ListenerArn { get; set; }
}
}
The above code expects everything to work without any issues and there are no try catch blocks. Best practice would be to have try catch blocks and send a failure response.
Following URLs can be referred for more details:
Custom resources
Custom resource request objects
Custom resource response objects
Using AWS Lambda with AWS CloudFormation
After changing the mapping to Automapper, only an empty list is sent through the endpoint.
Initially I had an endpoint that retrieved all employees with info including a list with every course each employee had taken. This was with manual mapping between entities & Dto.
//From startup.cs in Configure
AutoMapper.Mapper.Initialize(cfg =>
{
cfg.CreateMap<Employee, Models.EmployeeCoursesDto>();
cfg.CreateMap<Employee, Models.EmployeeDto>();
cfg.CreateMap<EmployeeCourses, Models.EmployeeCoursesDto>();
});
//From Employee entity
public class Employee
{
[Key]
//Gen new Id key in DB when object created
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Title { get; set; }
public ICollection<EmployeeCourses> EmployeeCourses { get; set; }
= new List<EmployeeCourses>();
}
}
//From employee Dto
public class EmployeeDto
{
public int Id { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public ICollection<EmployeeCoursesDto> EmployeeCourses { get; set; }
= new List<EmployeeCoursesDto>();
}
}
//Endpoint in controller
[HttpGet()]
public IActionResult GetAllEmployees()
{
var employeeEntities = _employeeInfoRepository.GetEmployees();
var results = Mapper.Map<IEnumerable<EmployeeDto>>(employeeEntities);
return Ok(results);
}
//From Irepository
IEnumerable<Employee> GetEmployees();
//From repository
public IEnumerable<Employee> GetEmployees()
{
return _context.Employees.OrderBy(c => c.Name).ToList();
}
I expected output all employees with all datafileds, including their list of courses.
The output is all fields with data, except the list of courses which is "0" when running with a breakpoint, and in Postman it shows as only:
"id": 2,
"name": "Test Person",
"title": "Bus Driver",
"numberOfCourses": 0,
"employeeCourses": [],
"totalAchievedHoursAuditor": 0,
"totalAchievedHoursAccountant": 51,
"courseBalanceAccountant": null,
"courseBalanceAuditor": null
However, if I try another endpoint only for retrieving a specific course, or a list of courses, the data show correctly. Seems there are an issue with mapping the employees & courses at the same time?
I found the error, not Automapper, but my Linq statement:
return _context.Employees.Include(c => c.EmployeeCourses).ToList();
Please close this thread. Thanks for the reply Lucian Bargaoanu & have a great weekend.
I have got response List<List<KeyValuePair<string, object>>> but I want to convert response into List<ClassName>.
KeyValuePair key and ClassName property both are same name and same type
What is the most programmatically way to convert response?
I have got response
My class structure
public class TestModel
{
public string TaxablePersonCode { get; set; }
public string LegalNameAsPerPan { get; set; }
public string TradeName { get; set; }
public string ConstitutionName { get; set; }
public string ResidentialStatusName { get; set; }
public string PrimaryMobileNo { get; set; }
public string FlatOrOfficeNo { get; set; }
public string TownOrCityOrDist { get; set; }
public string Pincode { get; set; }
public string StateName { get; set; }
public string CountryName { get; set; }
public string ContactName { get; set; }
public string ContactDesignationName { get; set; }
public string ContactMobile { get; set; }
public string ContactEmail { get; set; }
}
var listKeyValue = response.Select(x => x.Value).ToList();
var data = JsonConvert.DeserializeObject<List<TestModel>>(listKeyValue);
The only part of this im unsure about is when indexing the final list for the correct property, but you can choose what works best for you. Using a .Where() every time ensures you'll get the right result but it will search the list every single time and be a lot slower. If your %100 certain the order of the list will never change you could gain some performance by directly indexing the list for the element you want using [] or .ElementAt(). Anyway, heres what your looking for.
List<TestModel> myList = response.Select(x => new TestModel
{
// Using Where
TaxablePersonCode = x.Where(t => t.Key == "TaxablePersonCode").First().Value,
// Using direct index
LegalNameAsPerPan = x[1].Value,
// Using ElementAt
TradeName = x.ElementAt(2).Value,
...
});
Hope that helps!
Did you get some result with JsonConvert class? Did it worked for you? If not, you can try out something like this (if JSON and field are properly named):
var listKeyValue = response.Select(x => x.Value).ToList();
var result = new List<TestModel>();
foreach (var keyValueList in listKeyValue)
{
// convert the response list of KeyValuePair to dictionary
var dictionary = keyValueList.ToDictionary(kv => kv.Key, kv => kv.Value);
var tempModel = new TestModel();
// get actual value by name of the rpoperty
tempModel.TaxablePersonCode = dictionary[nameof(tempModel.TaxablePersonCode)].ToString();
// etc.
result.Add(tempModel);
}
Maybe this approach could be improved with reflection, but this will degrade the performance.
// get all properties to populate
var properties = typeof(TestModel).GetProperties(BindingFlags.Public | BindingFlags.Instance);
I see you're using JSON, so you should probably just deserialize the object properly, which I would expect to look something like this:
List<TestModel> models = JsonConvert.DeserializeObject<List<TestModel>>(response);
Otherwise, you could use reflection to bind the known KeyValuePair keys to the properties of the object; that being said, you will need to ensure that the return values are compatible with the values from the returned data, else this will fail.
outerList.ForEach(innerList => {
TestModel result = new TestModel();
innerList.ForEach(listItem => {
result
.GetType()
.GetProperty(listItem.Key)
?.SetValue(result, listItem.Value);
});
});
I'd like to replicate some dynamodb tables, schema only, into my local environment for testing purposes. First I've tried:
aws dynamodb describe-table --table-name Foo > FooTable.json
But it's obvious that the output schema is not compliant to the input schema from the create-table command:
aws dynamodb create-table --cli-input-json file://FooTable.json --endpoint=http://localhost:8000
What I'm trying to avoid is to generate dozens of skeletons with aws dynamodb create-table --generate-cli-skeleton and fill them manually :/
Is there a way to get the table schema in a format that is "useful" for recreation? I find it unbelievable that there are no straightforward way of doing it through the web graphic interface or the standard aws command line - after hearing how "good" was their service.
I just managed to do a complete dump and "restore" using bchew/dynamodump:
git clone git#github.com:bchew/dynamodump.git
Notice the --schemaOnly option in the documentation https://github.com/bchew/dynamodump. Command was:
./dynamodump.py -m backup --schemaOnly --region foo-region --host localhost --srcTable '*' --port 8000 --accessKey fooKey --secretKey barKey
Then you can use the -m restore mode to put the data or schema back into a local dynamodb or wherever desired :)
With that said, I still find it unbelievable how bad is the amazon dynamodb tool-chain. Come on guys.
This takes aws dynamodb describe-table output, and transforms it into the input-format of aws dynamodb create-table --cli-input-json:
AWS_PROFILE=xyz aws dynamodb describe-table --table-name MyTable > mytable_full.json
# Pull out just what is minimally needed for table-creation:
#
# TableName
# KeySchema
# AttributeDefinitions (must only contain attributes used in keys)
# Global/Local Secondary Indexes
# Defaults BillingMode to PAY_PER_REQUEST
# (any provisioning can be set up manually based on load)
jq <mytable_full.json '.Table | {TableName, KeySchema, AttributeDefinitions} + (try {LocalSecondaryIndexes: [ .LocalSecondaryIndexes[] | {IndexName, KeySchema, Projection} ]} // {}) + (try {GlobalSecondaryIndexes: [ .GlobalSecondaryIndexes[] | {IndexName, KeySchema, Projection} ]} // {}) + {BillingMode: "PAY_PER_REQUEST"}' >mytable.json
AWS_PROFILE=xyz aws dynamodb create-table --cli-input-json file://mytable.json
You can also paste the json into python (the python dict syntax closely matches json) eg
import boto3
dynamodb = boto3.resource("dynamodb")
tabledef = {
"TableName": "MyTable",
"KeySchema": [
...
}
table = dynamodb.create_table(**tabledef)
print("Table status: ", table.table_status)
References:
https://docs.aws.amazon.com/cli/latest/reference/dynamodb/describe-table.html
https://docs.aws.amazon.com/cli/latest/reference/dynamodb/create-table.html
https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_CreateTable.html
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html#creating-a-new-table
Here is a version using C#, AWS CLI and Newtonsoft JSON on Windows. Start by running this command: -
aws dynamodb describe-table --table-name TheTable --profile SourceAWSCredsProfile > TheTable.json
Pick up the file, deserialize and serialize to the --cli-input-json friendly class: -
TableContainer tableContainer;
string sourceFile = "TheTable.json";
string destFile = "TheTable.cli-input.json";
using (StreamReader file = File.OpenText(sourceFile))
{
JsonSerializer serializer = new JsonSerializer();
tableContainer = (TableContainer)serializer.Deserialize(file, typeof(TableContainer));
}
File.WriteAllText(destFile, JsonConvert.SerializeObject(tableContainer.Table, Formatting.Indented, new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
}
));
Now run this command to import the table definition: -
aws dynamodb create-table --cli-input-json file://TheTable.cli-input.json --profile DestinationAWSCredsProfile
The TableContainer class definition is below. The absence of certain properties cleans out everything that the --cli-input-json parameter doesn't need. You can recreate this class anytime by running: -
aws dynamodb create-table --generate-cli-skeleton
Then copy and paste the output into a new class file, using the very handy Paste Special... Paste JSON as Classes feature in Visual Studio.
public class TableContainer
{
public DynamoTableCLIJSON Table { get; set; }
}
public class DynamoTableCLIJSON
{
public Attributedefinition[] AttributeDefinitions { get; set; }
public string TableName { get; set; }
public Keyschema[] KeySchema { get; set; }
public Localsecondaryindex[] LocalSecondaryIndexes { get; set; }
public Globalsecondaryindex[] GlobalSecondaryIndexes { get; set; }
public string BillingMode { get; set; }
public Provisionedthroughput ProvisionedThroughput { get; set; }
public Streamspecification StreamSpecification { get; set; }
public Ssespecification SSESpecification { get; set; }
public Tag[] Tags { get; set; }
}
public class Provisionedthroughput
{
public int ReadCapacityUnits { get; set; }
public int WriteCapacityUnits { get; set; }
}
public class Streamspecification
{
public bool StreamEnabled { get; set; }
public string StreamViewType { get; set; }
}
public class Ssespecification
{
public bool Enabled { get; set; }
public string SSEType { get; set; }
public string KMSMasterKeyId { get; set; }
}
public class Attributedefinition
{
public string AttributeName { get; set; }
public string AttributeType { get; set; }
}
public class Keyschema
{
public string AttributeName { get; set; }
public string KeyType { get; set; }
}
public class Localsecondaryindex
{
public string IndexName { get; set; }
public Keyschema1[] KeySchema { get; set; }
public Projection Projection { get; set; }
}
public class Projection
{
public string ProjectionType { get; set; }
public string[] NonKeyAttributes { get; set; }
}
public class Keyschema1
{
public string AttributeName { get; set; }
public string KeyType { get; set; }
}
public class Globalsecondaryindex
{
public string IndexName { get; set; }
public Keyschema2[] KeySchema { get; set; }
public Projection1 Projection { get; set; }
public Provisionedthroughput1 ProvisionedThroughput { get; set; }
}
public class Projection1
{
public string ProjectionType { get; set; }
public string[] NonKeyAttributes { get; set; }
}
public class Provisionedthroughput1
{
public int ReadCapacityUnits { get; set; }
public int WriteCapacityUnits { get; set; }
}
public class Keyschema2
{
public string AttributeName { get; set; }
public string KeyType { get; set; }
}
public class Tag
{
public string Key { get; set; }
public string Value { get; set; }
}
Angular newbie and I have had no luck getting to the bottom of this one. I am trying to send an update call to my RESTful web service. I am passing and int and an object. When debugging back at the web service, the int comes in fine, however the object always come is as NULL. Debugging before the call is made shows that the object has a value. Why am I getting NULL for my object in the web service?
I have tried making the call from a factory as well as the save function. Both are null when the web service is called.
app.js
TournamentDirectorApp.factory('tournamentFactory', function () {
return {
addUserToTournament: function (id, tourney, user, Tournament) {
tourney.Users.push(user)
var response = Tournament.update({ id: id }, { tourney: tourney })
return tourney;
}
};
});
$scope.save = function () {
var updatedTournament = tournamentFactory.addUserToTournament(id, selectedTourney, $scope.selectedUser, Tournament);
Tournament.update({ id: id }, { tournament: updatedTournament }, function () {
$location.path('/')
});
};
web service
public HttpResponseMessage PutTournament(int id, Tournament tournament)
{
if (ModelState.IsValid && id == tournament.TournamentId)
{
db.Entry(tournament).State = EntityState.Modified;
db.SaveChanges();
return Request.CreateResponse(HttpStatusCode.OK);
}
else
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
* tournament object *
public class Tournament
{
public int TournamentId { get; set; }
public string Director { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public virtual List<Classification> Classifications { get; set; }
public virtual List<User> Users { get; set; }
}
* app.js (routing for the update) *
TournamentDirectorApp.factory('Tournament', function ($resource) {
return $resource('/api/tournament/:id', { id: '#id' }, {update: { method: 'PUT' }});
});
Based on the "update" example in the angular docs, I think the problem is that you should just send just the updatedTournament with the update request, not an object wrapping your changed object. Try:
Tournament.update({ id: id }, updatedTournament, function () {
$location.path('/') })
in your save function.