Reputation: 855
I'm trying to fetch firestore document using Rest API and it is returning data in below format
{"fields":{"list1":{"arrayValue":{"values":[{"stringValue":"item1"},{"stringValue":"item2"}]}}}}
how can I convert the above Document to plain JavaScript object like below and convert it back to firestore document before making the update call
{"list1":["item1","item2"]}
Screenshot of my data from Firestore console
Edit: More info
json sample1 is returned by Firestore api and has all datatype info
json sample2 is my actual data without type info.
My issue is firestore RestApi get call is returning the response in json sample1 format. I want to update some info in api response and send back updated values to firestore db. But due to all the type info in the response I'm not able to update the values where necessary. So I'm trying to find if there is a way to convert api response to json sample2.
API URL used : https://firestore.googleapis.com/v1/projects/projects/{project_id}/databases/{database_id}/documents/{document_path}
Upvotes: 5
Views: 3881
Reputation: 38378
How to convert a Firestore document into a normal JSON object:
function toValue(field) {
return "integerValue" in field
? Number(field.integerValue)
: "doubleValue" in field
? Number(field.doubleValue)
: "arrayValue" in field
? field.arrayValue.values.map(toValue)
: "mapValue" in field
? toJSON(field.mapValue)
: Object.entries(field)[0][1];
}
function toJSON(doc) {
return Object.fromEntries(
Object.entries(doc.fields ?? {}).map(([key, field]) => [key, toValue(field)])
);
}
Upvotes: 1
Reputation: 31
I just want to suggest a minor change to @mahindar's answer to handle 'nullValue', as Firestore recently allowed saving null values. Below is the updated code
let jsonToDocument = function (value) {
if (!value) {
return { 'nullValue': null };
} else if (!isNaN(value)) {
if (value.toString().indexOf('.') != -1)
return { 'doubleValue': value };
else
return { 'integerValue': value };
} else if (value === 'true' || value === 'false' || typeof value == 'boolean') {
return { 'booleanValue': value };
} else if (Date.parse(value)) {
return { 'timestampValue': value };
} else if (typeof value == 'string') {
return { 'stringValue': value };
} else if (value && value.constructor === Array) {
return { 'arrayValue': { values: value.map(v => jsonToDocument(v)) } };
} else if (typeof value === 'object') {
let obj = {};
for (let o in value) {
obj[o] = jsonToDocument(value[o]);
}
return { 'mapValue': { fields: obj } };
}
}
let documentToJson = function (fields) {
let result = {};
for (let f in fields) {
let key = f, value = fields[f],
isDocumentType = ['stringValue', 'booleanValue', 'doubleValue',
'integerValue', 'timestampValue', 'mapValue', 'arrayValue', 'nullValue'].find(t => t === key);
if (isDocumentType) {
let item = ['stringValue', 'booleanValue', 'doubleValue', 'integerValue', 'timestampValue', 'nullValue']
.find(t => t === key)
if (item)
return value;
else if ('mapValue' == key)
return documentToJson(value.fields || {});
else if ('arrayValue' == key) {
let list = value.values;
return !!list ? list.map(l => documentToJson(l)) : [];
}
} else {
result[key] = documentToJson(value)
}
}
return result;
}
let documentData = { "list1": { "arrayValue": { "values": [{ "stringValue": "item1" }, { "stringValue": "item2" }] } }, "another_value": { "nullValue": null } }
let jsonData = documentToJson(documentData);
let documentData1 = jsonToDocument(jsonData).mapValue.fields;
console.log(JSON.stringify(documentData));
console.log(JSON.stringify(jsonData));
console.log(JSON.stringify(documentData1));
Upvotes: 3
Reputation: 855
Seems with rest api only way is to write your own conversion. below code worked out for my requirement
let jsonToDocument = function (value) {
if (!isNaN(value)) {
if (value.toString().indexOf('.') != -1)
return { 'doubleValue': value };
else
return { 'integerValue': value };
} else if (value === 'true' || value === 'false' || typeof value == 'boolean') {
return { 'booleanValue': value };
} else if (Date.parse(value)) {
return { 'timestampValue': value };
} else if (typeof value == 'string') {
return { 'stringValue': value };
} else if (value && value.constructor === Array) {
return { 'arrayValue': { values: value.map(v => jsonToDocument(v)) } };
} else if (typeof value === 'object') {
let obj = {};
for (let o in value) {
obj[o] = jsonToDocument(value[o]);
}
return { 'mapValue': { fields: obj } };
}
}
let documentToJson = function (fields) {
let result = {};
for (let f in fields) {
let key = f, value = fields[f],
isDocumentType = ['stringValue', 'booleanValue', 'doubleValue',
'integerValue', 'timestampValue', 'mapValue', 'arrayValue'].find(t => t === key);
if (isDocumentType) {
let item = ['stringValue', 'booleanValue', 'doubleValue', 'integerValue', 'timestampValue']
.find(t => t === key)
if (item)
return value;
else if ('mapValue' == key)
return documentToJson(value.fields || {});
else if ('arrayValue' == key) {
let list = value.values;
return !!list ? list.map(l => documentToJson(l)) : [];
}
} else {
result[key] = documentToJson(value)
}
}
return result;
}
let documentData = { "list1": { "arrayValue": { "values": [{ "stringValue": "item1" }, { "stringValue": "item2" }] } } }
let jsonData = documentToJson(documentData);
let documentData1 = jsonToDocument(jsonData).mapValue.fields;
console.log(JSON.stringify(documentData));
console.log(JSON.stringify(jsonData));
console.log(JSON.stringify(documentData1));
Upvotes: 8
Reputation: 598847
To convert this input:
{"fields":{"list1":{"arrayValue":{"values":[{"stringValue":"item1"},{"stringValue":"item2"}]}}}}
To this output:
{"list1":["item1","item2"]}
You can do:
let input = {"fields":{"list1":{"arrayValue":{"values":[{"stringValue":"item1"},{"stringValue":"item2"}]}}}};
let expected = {"list1":["item1","item2"]};
let output = {};
Object.keys(input.fields).forEach((fieldName) => {
let arrayValue = input.fields[fieldName].arrayValue;
if (arrayValue) {
output[fieldName] = arrayValue.values.map((value) => value.stringValue);
}
});
console.log(output);
Upvotes: 0