How to flatten a nested JSON structure using Azure Data Factory - azure

I want to flatten my JSON with nested array object.
For example, my current JSON from Cosmos DB is:
[
{
"id": "",
"name": "",
"type": "",
"Data": [
{
"id": "",
"name": "aaa",
"value": "100"
},
{
"id": "",
"name": "bbb",
"value": "200"
}
]
}
]
I want to transform it to:
[
{
"id": "",
"name": "",
"type": "",
"aaa": "100",
"bbb": "200"
}
]
Basically, I want to use values of "Data.name" as key and "Data.value" as value in root structure.

You can achieve this using parse JSON and compose connectors in logic apps. After reproducing below is the logic app that worked for me.
I have initialized the variable in order to retrieve the Data.name and Data.Value.
In the above step, I was trying to retrieve all Data.name and Data.value values present in the JSON file.
and then I'm finally building the whole flattened JSON using compose connector.
RESULTS

Hi I’m Wayne Wang from the Microsoft for Founders Hub team!
I wrote this script using .net 5 function app with System.Text.Json 6.0.4 package
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.Json.Nodes;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
namespace FunctionApp2
{
public static class Function1
{
[Function("Function1")]
public static HttpResponseData Run([HttpTrigger(AuthorizationLevel.Function, "get", "post")] HttpRequestData req,
FunctionContext executionContext)
{
var logger = executionContext.GetLogger("Function1");
logger.LogInformation("C# HTTP trigger function processed a request.");
var stringInput = #"[
{
""id"": """",
""name"": """",
""type"": """",
""Data"": [
{
""id"": """",
""name"": ""aaa"",
""value"": ""100""
},
{
""id"": """",
""name"": ""bbb"",
""value"": ""200""
}
]
}
]"; //or you can get it from post;
var jr = JsonNode.Parse(stringInput);
var jcol = jr.AsArray().Select(arrayItem =>
{
var obj = arrayItem.AsObject();
var rval = new JsonObject();
CopyValue(obj, rval, "id");
CopyValue(obj, rval, "name");
CopyValue(obj, rval, "type");
if (obj.TryGetPropertyValue("Data", out var pnode))
{
var dataArray = pnode.AsArray();
foreach (var itemDataObject in dataArray.Select(x => x.AsObject()))
{
if (itemDataObject.TryGetPropertyValue("name", out var namep))
{
if (itemDataObject.TryGetPropertyValue("value", out var valuep))
{
rval.Add(namep.GetValue<string>(), valuep.GetValue<string>());
}
}
}
}
return rval;
});
var newjr = new JsonArray(jcol.ToArray());
var response = req.CreateResponse(HttpStatusCode.OK);
response.Headers.Add("Content-Type", "text/plain; charset=utf-8");
response.WriteString(newjr.ToJsonString());
return response;
}
private static void CopyValue(JsonObject from, JsonObject to, string propName)
{
if (from.TryGetPropertyValue(propName, out var pnode))
{
to.Add(propName, pnode.GetValue<string>());
}
}
}
}

Related

How do I modify all occurrences of a property (including nested) in an object before sending a JSON response?

I have an object created by parsing some JSON. The JSON data looks like this:
{
"id": "fan",
"name": "My super awesome fan",
"image": "Icon.png",
"details": {
"parts": [
{
"name": "base",
"image": "base.png"
},
{
"name": "blades",
"image": "blade.png"
}
],
"sale": {
"value": "prime",
"image": "PrimeDay.png"
}
}
}
The values in the image property point to files located under the directory "public" which is served using express.static. For example, the blade.png file is located at:
public
--products
--fan (this is the same as the id property)
--blade.png
Now, when a query is made to fetch the details of this object, I want to modify the image property of the object sent in the response so that the JSON response looks like:
{
"id": "fan",
"name": "My super awesome fan",
"image": "http://localhost:3000/products/fan/icon.png",
"details": {
"parts": [
{
"name": "base",
"image": "http://localhost:3000/products/fan/base.png"
},
{
"name": "blades",
"image": "http://localhost:3000/products/fan/blade.png"
}
],
"sale": {
"value": "prime",
"image": "http://localhost:3000/products/fan/PrimeDay.png"
}
}
}
I went down the path of using an express middleware to create a url to point to the path of the using
const url = req.protocol + '://' + req.get('host')
How do I use this url value to modify the response object?
I don't want to hardcode the full localhost path to the images in the JSON, since the path may change when deployed.
I was able to solve this, partly using Charly's solution. The only reason I could not use it fully was because the fan part of the URL is also dynamic and is dependent on the id property in the JSON data.
Here's my approach:
setImagePaths = (data) => {
const baseUrl = 'http://localhost:3000/products/'
const baseId = data.id;
const patchImagePaths = (obj) => {
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
return patchImagePaths(obj[key]);
}
if (key === 'image') {
obj[key] = baseUrl + baseId + '/' + obj[key];
}
});
}
patchImagePaths(data);
}
You could modify your response object with a recursive function to update the image properties:
var myObject = {
"id": "fan",
"name": "My super awesome fan",
"image": "Icon.png",
"details": {
"parts": [
{
"name": "base",
"image": "base.png"
},
{
"name": "blades",
"image": "blade.png"
}
],
"sale": {
"value": "prime",
"image": "PrimeDay.png"
}
}
}
;
const url = "http://localhost:3000/products/fan/";
//You can use here your url function with whatever output it has.
function resetImages (obj) {
Object.keys(obj).forEach(function (key) {
if (Array.isArray(obj[key])) {
let arr = obj[key];
arr.map((i)=>{
return i.image = url+i.image;
});
return arr;
}
if (typeof obj[key] === 'object') {
return resetImages(obj[key]);
}
if(key==="image") {
obj[key]=url+obj[key];
}
});
}
resetImages(myObject);
console.log(JSON.stringify(myObject));
**Output:**
{"id":"fan","name":"My super awesome fan","image":"http://localhost:3000/products/fan/Icon.png","details":{"parts":[{"name":"base","image":"http://localhost:3000/products/fan/base.png"},{"name":"blades","image":"http://localhost:3000/products/fan/blade.png"}],"sale":{"value":"prime","image":"http://localhost:3000/products/fan/PrimeDay.png"}}}

Can we add text field dynamically

I've created an adaptive card(using json) in my chatbot that takes input from users. I want to add a button that enables the user to add a new text field every time the user clicks on the insert field. (i.e., the user can click on insert button to enter details of education (school, college etc.))
Can this be achieved in adaptive cards?
I also wanted to know, can adaptive cards be designed in any other language (excluding json)
The easiest way to do this is with Action.ShowCard:
{
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 1",
"id": "text1"
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Add field",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 2",
"id": "text2"
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Add field",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 3",
"id": "text3"
}
],
"actions": [
{
"type": "Action.ShowCard",
"title": "Add field",
"card": {
"type": "AdaptiveCard",
"body": [
{
"type": "Input.Text",
"placeholder": "Placeholder 4",
"id": "text4"
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json"
}
}
],
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"version": "1.0"
}
You may not like the way that looks, but there is an alternative. Microsoft Teams allows you to update messages, so you can update the card with more input fields in response to a submit action. First, you'll need a way of saving state for your card so you can update the card's activity. In C# you can declare a state property accessor like this:
public IStatePropertyAccessor<Dictionary<string, (string ActivityId, int InputCount)>> InputCardStateAccessor { get; internal set; }
Then you can instantiate it like this:
InputCardStateAccessor = _conversationState.CreateProperty<Dictionary<string, (string, int)>>("cardState");
In Node.js you won't need to declare anything but you can instantiate it like this:
this.inputCardState = this.conversationState.createProperty('cardState');
You'll want a consistent way to generate your card that you can use when you send the card initially and when you update the card. I'm using the AdaptiveCards NuGet package in C#:
public static IActivity GenerateAdaptiveCardActivityWithInputs(int inputCount, object valueObject)
{
var cardData = JObject.FromObject(valueObject);
var cardId = Convert.ToString(cardData[KEYCARDID]);
var card = new AdaptiveCard(new AdaptiveSchemaVersion(1, 0))
{
Body = Enumerable.Range(0, inputCount).Select(i =>
{
var inputId = $"text{i}";
return new AdaptiveTextInput
{
Id = inputId,
Value = Convert.ToString(cardData[inputId]),
};
}).ToList<AdaptiveElement>(),
Actions = new List<AdaptiveAction>
{
new AdaptiveSubmitAction
{
Title = "Add field",
Data = new Dictionary<string, string>
{
{ KEYCARDID, cardId },
{ KEYSUBMITACTIONID, ACTIONSUBMITADDFIELD },
},
},
new AdaptiveSubmitAction
{
Title = "Submit",
},
},
};
return MessageFactory.Attachment(new Attachment(AdaptiveCard.ContentType, content: JObject.FromObject(card)));
}
Node.js:
generateAdaptiveCardActivityWithInputs(inputCount, cardData) {
var cardId = cardData[KEYCARDID];
var body = [];
for (let i = 0; i < inputCount; i++) {
var inputId = `text${i}`;
body.push({
type: "Input.Text",
id: inputId,
value: cardData[inputId]
});
}
var card = {
type: "AdaptiveCard",
$schema: "http://adaptivecards.io/schemas/adaptive-card.json",
version: "1.0",
body,
actions: [
{
type: "Action.Submit",
title: "Add field",
data: {
[KEYCARDID]: cardId,
[KEYSUBMITACTIONID]: ACTIONSUBMITADDFIELD
},
},
{
type: "Action.Submit",
title: "Submit",
}
]
};
return MessageFactory.attachment(CardFactory.adaptiveCard(card));
}
Using this function, you can send the card initially like this in C#:
var inputCount = 1;
var cardId = Guid.NewGuid().ToString();
var reply = GenerateAdaptiveCardActivityWithInputs(inputCount, new Dictionary<string, string> { { KEYCARDID, cardId } });
var response = await turnContext.SendActivityAsync(reply, cancellationToken);
var dict = await InputCardStateAccessor.GetAsync(turnContext, () => new Dictionary<string, (string, int)>(), cancellationToken);
dict[cardId] = (response.Id, inputCount);
Node.js:
var inputCount = 1;
var cardId = Date.now().toString();
var reply = this.generateAdaptiveCardActivityWithInputs(inputCount, { [KEYCARDID]: cardId });
var response = await turnContext.sendActivity(reply);
var dict = await this.inputCardState.get(turnContext, {});
dict[cardId] = {
activityId: response.id,
inputCount: inputCount
};
await this.inputCardState.set(turnContext, dict);
And you can update the card in response to the card's "add field" submit action like this in C#:
private async Task AddFieldAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
var activity = turnContext.Activity;
if (activity.ChannelId == Channels.Msteams)
{
var value = JObject.FromObject(activity.Value);
var cardId = Convert.ToString(value[KEYCARDID]);
var dict = await InputCardStateAccessor.GetAsync(turnContext, () => new Dictionary<string, (string, int)>(), cancellationToken);
if (dict.TryGetValue(cardId, out var cardInfo))
{
var update = GenerateAdaptiveCardActivityWithInputs(++cardInfo.InputCount, value);
update.Id = cardInfo.ActivityId;
update.Conversation = activity.Conversation;
await turnContext.UpdateActivityAsync(update, cancellationToken);
dict[cardId] = cardInfo;
}
}
}
Node.js:
async addField(turnContext) {
var activity = turnContext.activity;
if (activity.channelId == 'msteams') {
var value = activity.value;
var cardId = value[KEYCARDID];
var dict = await this.inputCardState.get(turnContext, {});
var cardInfo = dict[cardId];
if (cardInfo) {
var update = this.generateAdaptiveCardActivityWithInputs(++cardInfo.inputCount, value);
update.id = cardInfo.activityId;
update.conversation = activity.conversation;
update.serviceUrl = activity.serviceUrl;
dict[cardId] = cardInfo;
await this.inputCardState.set(turnContext, dict);
await turnContext.updateActivity(update);
}
}
}
yes this is possible you can look about the addRow in javascript

How to call Web3js function from Nodejs file

I have the following working web3js code, Calling App.createContract() on button click works well on a webpage, however I want to call App.createContract() or similar from another Nodejs controller. Infact what I am thinking of is making an API in Node which could call the web3js function and returns a JSON result back to the caller. Can you please help me how to import my web3js file and call the function from Node Controller? Thanks
import "../stylesheets/app.css";
import { default as Web3} from 'web3';
import { default as contract } from 'truffle-contract';
import { default as CryptoJS} from 'crypto-js';
var accounts;
var account;
var shLogABI;
var shLogContract;
var shLogCode;
var shLogSource;
window.App = {
start: function() {
var self = this;
web3.eth.getAccounts(function(err, accs) {
if (err != null) {
alert("There was an error fetching your accounts.");
return;
}
if (accs.length == 0) {
alert("Couldn't get any accounts! Make sure your Ethereum client is configured correctly.");
return;
}
accounts = accs;
console.log(accounts);
account = accounts[0];
web3.eth.defaultAccount= account;
shLogSource= "pragma solidity ^0.4.6; contract SHLog { struct LogData{ string FileName; uint UploadTimeStamp; string AttestationDate; } mapping(uint => LogData) Trail; uint8 TrailCount=0; function AddNewLog(string FileName, uint UploadTimeStamp, string AttestationDate) { LogData memory newLog; newLog.FileName = FileName; newLog.UploadTimeStamp= UploadTimeStamp; newLog.AttestationDate= AttestationDate; Trail[TrailCount] = newLog; TrailCount++; } function GetTrailCount() returns(uint8){ return TrailCount; } function GetLog(uint8 TrailNo) returns (string,uint,string) { return (Trail[TrailNo].FileName, Trail[TrailNo].UploadTimeStamp, Trail[TrailNo].AttestationDate); } }";
web3.eth.compile.solidity(shLogSource, function(error, shLogCompiled){
shLogABI = JSON.parse(' [ { "constant": false, "inputs": [ { "name": "TrailNo", "type": "uint8" } ], "name": "GetLog", "outputs": [ { "name": "", "type": "string" }, { "name": "", "type": "uint256" }, { "name": "", "type": "string" } ], "payable": false, "type": "function" }, { "constant": false, "inputs": [ { "name": "FileName", "type": "string" }, { "name": "UploadTimeStamp", "type": "uint256" }, { "name": "AttestationDate", "type": "string" } ], "name": "AddNewLog", "outputs": [], "payable": false, "type": "function" }, { "constant": false, "inputs": [], "name": "GetTrailCount", "outputs": [ { "name": "", "type": "uint8" } ], "payable": false, "type": "function" } ]');
shLogContract = web3.eth.contract(shLogABI);
});
});
},
createContract: function()
{
shLogContract.new("", {from:account, gas: 3000000}, function (error, deployedContract){
if(deployedContract.address)
{
document.getElementById("contractAddress").value=deployedContract.address;
document.getElementById("fileName").value = '';
document.getElementById("uploadTimeStamp").value = '';
document.getElementById("attestationDate").value = '';
}
})
},
addNewLog: function()
{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
var fileName = document.getElementById("fileName").value;
var uploadTimeStamp = document.getElementById("uploadTimeStamp").value;
var attestationDate = document.getElementById("attestationDate").value;
deployedshLog.AddNewLog(fileName, uploadTimeStamp, attestationDate, function(error){
console.log(error);
})
},
getLog: function()
{
try{
var contractAddress = document.getElementById("contractAddress").value;
var deployedshLog = shLogContract.at(contractAddress);
deployedshLog.GetTrailCount.call(function (error, trailCount){
deployedshLog.GetLog.call(trailCount-1, function(error, returnValues){
document.getElementById("fileName").value= returnValues[0];
document.getElementById("uploadTimeStamp").value = returnValues[1];
document.getElementById("attestationDate").value=returnValues[2];
})
})
}
catch (error) {
document.getElementById("fileName").value= error.message;
document.getElementById("uploadTimeStamp").value = error.message;
document.getElementById("attestationDate").value= error.message;
}
}
};
window.addEventListener('load', function() {
if (typeof web3 !== 'undefined') {
console.warn("Using web3 detected from external source. If using MetaMask, see the following link. Feel free to delete this warning. :) http://truffleframework.com/tutorials/truffle-and-metamask")
window.web3 = new Web3(web3.currentProvider);
} else {
console.warn("No web3 detected. Falling back to http://localhost:8545. You should remove this fallback when you deploy live, as it's inherently insecure. Consider switching to Metamask for development. More info here: http://truffleframework.com/tutorials/truffle-and-metamask");
// fallback - use your fallback strategy (local node / hosted node + in-dapp id mgmt / fail)
window.web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
}
App.start();
});
Simple example for web3js 1.0.0.beta*
1) Add web3js to your package.json on a server side:
"web3": "^1.0.0-beta.27"
2) Require and init web3 with some provider:
const Web3 = require('web3');
const web3SocketProvider = new Web3.providers.WebsocketProvider('ws://0.0.0.0:8546');
const web3Obj = new Web3(web3Provider);
Now you can use your web3 object as usual:
async function getAccounts() {
let accounts = await web3Obj.eth.getAccounts();
}

DynamoDB putitem in NodeJs - arrays of objects

I'm trying to set up a small api from AWS Lambda to DynamoDB and I am having trouble figuring out if and how I can write an array of objects into a key.
I have an object like
{
"teamName": "Team Awesome",
"members": [
{
"email": "person-1#example.com",
"name": "Bob"
},
{
"email": "person-2#example.com",
"name": "Alice"
}
]
}
The members array is giving me issues, in the docs it looks like it can be done considering the list types, but there is just no example how HOW to do it, and I am running out of ways to try it.
So is it possible to write something in this format at all and how do you in that case do it?
Example code - what do I put at ???
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB();
exports.handler = function(event, context) {
var tableName = "GDCCompetition";
var datetime = new Date().getTime().toString();
DynamoDB.putItem({
"TableName": tableName,
"Item": {
"datetime": {
"N": datetime
},
"teamName": {
"S": event.teamName
},
"members": ???
}
});
}
The documentation is not really obvious, but there is a thing called DocClient, you can pass a usual JS object to it and it will do all the parsing and transformation into AWS object (with all the types). You can use it like this:
var AWS = require("aws-sdk");
var DynamoDB = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: "MyTable",
Item: {
"teamName": "Team Awesome",
"members": [
{
"email": "person-1#example.com",
"name": "Bob"
},
{
"email": "person-2#example.com",
"name": "Alice"
}
]
}
};
DynamoDB.put(params, function (err) {
if (err) {
return throw err;
}
//this is put
});
You could convert the object to DynamoDb record first
const AWS = require('aws-sdk');
var tableName = "GDCCompetition";
var datetime = new Date().getTime().toString();
const members = [
{
"email": "person-1#example.com",
"name": "Bob"
},
{
"email": "person-2#example.com",
"name": "Alice"
}
];
const marshalled = AWS.DynamoDB.Converter.marshall({ members });
const params = {
"TableName": tableName,
"Item": {
"datetime": {
"N": datetime
},
"teamName": {
"S": event.teamName
},
"members": marshalled.members,
},
}
AWS.DynamoDB.putItem(params, function (err) {
if (err) {
return throw err;
}
});

How to define Array name while returning JSON output

Hello I am very new in JSON. I have written a method which is returning JSON output like below:
[
{
"PostId": 0,
"Title": "BCS",
"ImageInfo": null,
"ShortDescription": null,
"Created": "0001-01-01T00:00:00"
},
{
"PostId": 0,
"Title": "ABC",
"ImageInfo": null,
"ShortDescription": "Corruption",
"Created": "0001-01-01T00:00:00"
}
]
My method which is returning output is below:
public string GetPost(GetPost userPost)
{
var objPosts = from p in _dbcontext.Posts
where p.CategoryId == userPost.CategoryId orderby p.PostId descending
select new
{
Title = p.Title,
ImageInfo = p.ImageInfo,
ShortDescription = p.ShortDescription,
};
var listEmail = new List<Post>();
foreach (var item in objPosts)
{
var objresult = new Post
{
Title = item.Title,
ImageInfo = item.ImageInfo,
ShortDescription = item.ShortDescription
};
listEmail.Add(objresult);
}
string output = JsonConvert.SerializeObject(listEmail);
return output;
}
I actually want to add the array name, so that my JSON ouput can be easily read. I want something like below:
{
"contacts": [
{
"PostId": 0,
"Title": "BCS",
"ImageInfo": null,
"ShortDescription": null,
"Created": "0001-01-01T00:00:00"
},
{
"PostId": 0,
"Title": "BCS",
"ImageInfo": null,
"ShortDescription": null,
"Created": "0001-01-01T00:00:00"
}
]
}
Please help.
Just change the list into dictionary like this:
var dictionaryEmail = new Dictionary<string, List<Post>>();
var listEmail = var listEmail = new List<Post>();;
foreach (var item in objPosts)
{
var objresult = new Post
{
Title = item.Title,
ImageInfo = item.ImageInfo,
ShortDescription = item.ShortDescription
};
listEmail.Add(objresult);
}
dictionaryEmail.add("Contacts", listEmail);
string output = JsonConvert.SerializeObject(dictionaryEmail);
return output;

Resources