InversifyJS : Error when retrieving object from container - node.js

I am using InversifyJS to setup some kind-of dynamic binding and running it on NodeJS. The problem I have is that I get errors, but without messages and only a stacktrace.
The Main class
export class Main {
public static getCalculator(config: string): Calculator {
try {
var container: Container = new KeyFeatureContainer(config).getContainer();
debugger;
// error here !
return container.get<Calculator>(TYPES.Calculator);
} catch (error) {
debugger;
return null;
}
}
}
This class calls the KeyFeatureContainer with a json string, used to configure all the bindings (in a sort-of dynamic way), and then retrieves the container.
The KeyFeatureContainer class
export class KeyFeatureContainer {
private _container: Container;
public constructor(config: string) {
var jsonConfig: any[] = JSON.parse(config);
this._container = new Container();
this._container.bind<Calculator>(TYPES.Calculator).to(KeyFeatureCalculator);
for (var i = 0; i < jsonConfig.length; i++) {
if (jsonConfig[i].active) {
this.parseConfigKeyFeatures(jsonConfig[i].id);
this.parseConfigParams(jsonConfig[i].params);
}
}
}
public getContainer(): Container {
debugger;
return this._container;
}
private parseConfigKeyFeatures(id: string): void {
var keyFeatureContainerModule: ContainerModule = rootContainer.get<KeyFeatureContainerModule>(id).getContainerModule();
if (keyFeatureContainerModule != null)
this._container.load(keyFeatureContainerModule);
}
private parseConfigParams(params: Array<{ name: string, value: any }>): void {
for (var param of params)
this._container.bind(param.name).to(param.value);
}
}
This class receives the json configuration and loads, following an ID, a ContainerModule which contains the binding between the interface and the concrete implementation. The rootContainer specifies the ContainerModule to retrieve following an ID.
The rootContainer
var rootContainer: Container = new Container();
rootContainer.bind<KeyFeatureContainerModule>(KEYFEATURES.DrugHoliday).to(DrugHolidayContainerModule);
rootContainer.bind<KeyFeatureContainerModule>(KEYFEATURES.MissingDay).to(MissingDayContainerModule);
export default rootContainer;
And the related ContainerModules (the second one is identical, just the PARAMS that are different for each)
#injectable()
export class MissingDayKeyFeature implements KeyFeature {
#inject(PARAMS.MissingDayParams.NbIntakesLimit)
private _nbIntakesLimit: number;
#inject(PARAMS.MissingDayParams.ExtraParamA)
private _extraParamA: any;
#inject(PARAMS.MissingDayParams.ExtraParamB)
private _extraParamB: any;
public init(): void {
console.log("init() at MissingDay");
console.log("nbIntakesLimit = " + this._nbIntakesLimit);
console.log("extraParamA = " + this._extraParamA);
console.log("extraParamB = " + this._extraParamB);
}
public calculate(): void {
console.log("calculate() at MissingDay");
}
public finish(): void {
console.log("finish() at MissingDay");
}
}
#injectable()
export class MissingDayContainerModule implements KeyFeatureContainerModule {
public getContainerModule(): ContainerModule {
return new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Unbind) => {
bind<KeyFeature>(TYPES.KeyFeature).to(MissingDayKeyFeature);
});
}
}
And finally, the ServiceIdentifiers used to setup the whole (separated files)
let TYPES = {
KeyFeature: "KeyFeature",
Calculator: "Calculator"
}
export default TYPES;
let PARAMS = {
DrugHolidayParams: {
NbDaysLimit: "nbDaysLimit",
ExtraParamA: "extraParamDHA",
ExtraParamB: "extraParamDHB"
},
MissingDayParams: {
NbIntakesLimit: "nbIntakesLimit",
ExtraParamA: "extraParamMDA",
ExtraParamB: "extraParamMDB"
}
}
export default PARAMS;
let KEYFEATURES = {
MissingDay: "MissingDayKeyFeature",
DrugHoliday: "DrugHolidayKeyFeature"
}
export default KEYFEATURES;
Finally the JSON input (formatted for convenience)
[{
"id": "DrugHolidayKeyFeature",
"active": true,
"params": [{
"name": "nbDaysLimit",
"value": 3
}, {
"name": "extraParamDHA",
"value": "DHA"
}, {
"name": "extraParamDHB",
"value": "DHB"
}
]
}, {
"id": "MissingDayKeyFeature",
"active": false,
"params": [{
"name": "nbIntakesLimit",
"value": 0
}, {
"name": "extraParamMDA",
"value": "MDA"
}, {
"name": "extraParamMDB",
"value": "MDB"
}
]
}
]
I use a simple Test script just to output the values and see if all bindings are done correctly
import { Main } from "./Main";
var json: string = *json above*;
Main.getCalculator(json).calculate();
But I end up with this error
Error
< at _createSubRequests (D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\node_modules\inversify\lib\planning\planner.js:106:19)
< at Object.plan (D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\node_modules\inversify\lib\planning\planner.js:125:5)
< at D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\node_modules\inversify\lib\container\container.js:205:37
< at Container._get (D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\node_modules\inversify\lib\container\container.js:198:44)
< at Container.get (D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\node_modules\inversify\lib\container\container.js:163:21)
< at Function.getCalculator (D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\transpiled\KFCalc\Main.js:9:30)
< at Object.<anonymous> (D:\Projects\226RD\nodejs\phantomjs_2016_by_DM\final\transpiled\KFCalc\Test.js:4:13)
< at Module._compile (module.js:573:32)
< at Object.Module._extensions..js (module.js:582:10)
< at Module.load (module.js:490:32)
There is no clue about what is happening, and I can't figure out what I have done wrong. I can't get<Calculator> nor get<KeyFeature>, but if I do :
container.isBound(TYPES.Calculator) // returns true !!!
and same applies for all bound items.
Thank you for any pointers, I'm out of ideas.
+-- #types/node#7.0.2
+-- inversify#3.0.0
+-- reflect-metadata#0.1.9
+-- typescript#2.0.10
EDIT : Forgot the KeyFeatureCalculator
#injectable()
export class KeyFeatureCalculator implements Calculator {
// multi-injection of bound key features
private _keyFeatureCalculators: KeyFeature[] = [];
public constructor(#multiInject(TYPES.KeyFeature) keyFeatureCalculators: KeyFeature[]) {
this._keyFeatureCalculators = keyFeatureCalculators;
}
public calculate(): void {
console.log("calculate() at KeyFeatureCalculator");
for (var calculator of this._keyFeatureCalculators) {
calculator.init();
calculator.calculate();
calculator.finish();
}
}
}
Also, all my files (or almost) import inject, injectable from inversify and also import reflect-metadata

Error resolved : For those who didn't pay attention, each KeyFeature object has its properties injected, and their types are number or any. Since the values are retrieved from my JSON, they are typed as any and thus the compiler doesn't warn me that the binding bind(xxx).to(yyy) requires to be a newable element !
So, in my example, the yyy was something like 3, which is a number and thus not boundable. I changed to bind(xxx).toConstantValue(yyy) and everything works !

Related

Entity Framework Core 6 - trim all returned strings by command interception

When I was working with EF (System.Data.Entity) I successfully used an interceptor to automatically trim all existing strings in the database.
The IDbCommandTreeInterceptor is described in this post: EF6.1–Workaround Trailing Blanks Issue in String Joins.
public class StringTrimmerInterceptor : IDbCommandTreeInterceptor
{
public void TreeCreated(DbCommandTreeInterceptionContext interceptionContext)
{
if (interceptionContext.OriginalResult.DataSpace == DataSpace.SSpace)
{
var queryCommand = interceptionContext.Result as DbQueryCommandTree;
if (queryCommand != null)
{
var newQuery = queryCommand.Query.Accept(new StringTrimmerQueryVisitor());
interceptionContext.Result = new DbQueryCommandTree(
queryCommand.MetadataWorkspace,
queryCommand.DataSpace,
newQuery);
}
}
}
private class StringTrimmerQueryVisitor : DefaultExpressionVisitor
{
private static readonly string[] _typesToTrim = { "nvarchar", "varchar", "char", "nchar" };
public override DbExpression Visit(DbNewInstanceExpression expression)
{
var arguments = expression.Arguments.Select(a =>
{
var propertyArg = a as DbPropertyExpression;
if (propertyArg != null && _typesToTrim.Contains(propertyArg.Property.TypeUsage.EdmType.Name))
return EdmFunctions.Trim(a);
return a;
});
return DbExpressionBuilder.New(expression.ResultType, arguments);
}
}
}
I need some help to implement the same functionality by EntityFrameworkCore DbCommandInterceptor.

How to use a function put in using a constructor

So I have this class:
class Sigil {
constructor(name = "", type = "", func = (oppositeCard, oppositeCardLane) => { }) {
this.name = name;
this.type = type;
this.function = func
}
Activate(oppositeCard, oppositeCardLane) {
};
}
var fly = new Sigil("Fly", "OnAttack", (oppositeCard, oppositeCardLane) => {
oppositeCard = cardLib.blank;
return oppositeCard;
})
What I wan to do is use that function that I put in using the constructor. So like i want to call the "func" function using the Activate method
All you do is call this.function.
class Sigil {
...
Activate(){
this.function();
}
...
}

Loading indicator does not hide if api failed to retrieve data although it hides if api succeed to retrieve data in Android Paging library

I have a remote server from where I want to fetch 20 items(Job) per api call and show them in RecyclerView using paging library.
For that, I want to show a loading indicator at the beginning of the first api call when list of items is being fetched from the server. Everything is okay if data is fetched successfully. That means the loading indicator got invisible if data loaded successfully. The code is given bellow.
JobService.KT
#GET(Constants.API_JOB_LIST)
fun getJobPost(
#Query("page") pageNumber: Int
): Observable<Response<JobResponse>>
JobResponse.kt
data class JobResponse(
#SerializedName("status") val status: Int? = null,
#SerializedName("message") val message: Any? = null,
#SerializedName("data") val jobData: JobData? = null
)
JobData.kt
data class JobData(
#SerializedName("jobs") val jobs: List<Job?>? = null,
#SerializedName("total") val totalJob: Int? = null,
#SerializedName("page") val currentPage: Int? = null,
#SerializedName("showing") val currentlyShowing: Int? = null,
#SerializedName("has_more") val hasMore: Boolean? = null
)
NetworkState.kt
sealed class NetworkState {
data class Progress(val isLoading: Boolean) : NetworkState()
data class Failure(val errorMessage: String?) : NetworkState()
companion object {
fun loading(isLoading: Boolean): NetworkState = Progress(isLoading)
fun failure(errorMessage: String?): NetworkState = Failure(errorMessage)
}
}
Event.kt
open class Event<out T>(private val content: T) {
private var hasBeenHandled = false
fun getContentIfNotHandled() = if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
fun peekContent() = content
}
JobDataSource.kt
class JobDataSource(
private val jobService: JobService,
private val compositeDisposable: CompositeDisposable
) : PageKeyedDataSource<Int, Job>() {
val paginationState: MutableLiveData<Event<NetworkState>> = MutableLiveData()
val initialLoadingState: MutableLiveData<Event<NetworkState>> = MutableLiveData()
val totalJob: MutableLiveData<Event<Int>> = MutableLiveData()
companion object {
private const val FIRST_PAGE = 1
}
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, Job>) {
compositeDisposable += jobService.getJobPost(FIRST_PAGE)
.performOnBackgroundOutputOnMain()
.doOnSubscribe { initialLoadingState.postValue(Event(loading(true))) }
.doOnTerminate { initialLoadingState.postValue(Event(loading(false))) }
.subscribe({
if (it.isSuccessful) {
val jobData = it.body()?.jobData
totalJob.postValue(Event(jobData?.totalJob!!))
jobData.jobs?.let { jobs -> callback.onResult(jobs, null, FIRST_PAGE+1) }
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
CUSTOM_STATUS_CODE -> initialLoadingState.postValue(Event(failure(error.message!!)))
else -> initialLoadingState.postValue(Event(failure("Something went wrong")))
}
}
}, {
if (it is IOException) {
initialLoadingState.postValue(Event(failure("Check Internet Connectivity")))
} else {
initialLoadingState.postValue(Event(failure("Json Parsing error")))
}
})
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Job>) {
compositeDisposable += jobService.getJobPost(params.key)
.performOnBackgroundOutputOnMain()
.doOnSubscribe { if (params.key != 2) paginationState.postValue(Event(loading(true))) }
.doOnTerminate { paginationState.postValue(Event(loading(false))) }
.subscribe({
if (it.isSuccessful) {
val jobData = it.body()?.jobData
totalJob.postValue(Event(jobData?.totalJob!!))
jobData.jobs?.let { jobs -> callback.onResult(jobs, if (jobData.hasMore!!) params.key+1 else null) }
} else {
val error = Gson().fromJson(it.errorBody()?.charStream(), ApiError::class.java)
when (it.code()) {
CUSTOM_STATUS_CODE -> initialLoadingState.postValue(Event(failure(error.message!!)))
else -> initialLoadingState.postValue(Event(failure("Something went wrong")))
}
}
}, {
if (it is IOException) {
paginationState.postValue(Event(failure("Check Internet Connectivity")))
} else {
paginationState.postValue(Event(failure("Json Parsing error")))
}
})
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Job>) {}
}
JobDataSourceFactory.kt
class JobDataSourceFactory(
private val jobService: JobService,
private val compositeDisposable: CompositeDisposable
): DataSource.Factory<Int, Job>() {
val jobDataSourceLiveData = MutableLiveData<JobDataSource>()
override fun create(): DataSource<Int, Job> {
val jobDataSource = JobDataSource(jobService, compositeDisposable)
jobDataSourceLiveData.postValue(jobDataSource)
return jobDataSource
}
}
JobBoardViewModel.kt
class JobBoardViewModel(
private val jobService: JobService
) : BaseViewModel() {
companion object {
private const val PAGE_SIZE = 20
private const val PREFETCH_DISTANCE = 20
}
private val jobDataSourceFactory: JobDataSourceFactory = JobDataSourceFactory(jobService, compositeDisposable)
var jobList: LiveData<PagedList<Job>>
init {
val config = PagedList.Config.Builder()
.setPageSize(PAGE_SIZE)
.setInitialLoadSizeHint(PAGE_SIZE)
.setPrefetchDistance(PREFETCH_DISTANCE)
.setEnablePlaceholders(false)
.build()
jobList = LivePagedListBuilder(jobDataSourceFactory, config).build()
}
fun getPaginationState(): LiveData<Event<NetworkState>> = Transformations.switchMap<JobDataSource, Event<NetworkState>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::paginationState
)
fun getInitialLoadingState(): LiveData<Event<NetworkState>> = Transformations.switchMap<JobDataSource, Event<NetworkState>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::initialLoadingState
)
fun getTotalJob(): LiveData<Event<Int>> = Transformations.switchMap<JobDataSource, Event<Int>>(
jobDataSourceFactory.jobDataSourceLiveData,
JobDataSource::totalJob
)
}
JobBoardFragment.kt
class JobBoardFragment : BaseFragment() {
private val viewModel: JobBoardViewModel by lazy {
getViewModel { JobBoardViewModel(ApiFactory.jobListApi) }
}
private val jobAdapter by lazy {
JobAdapter {
val bundle = Bundle()
bundle.putInt(CLICKED_JOB_ID, it.jobId!!)
navigateTo(R.id.jobBoard_to_jobView, R.id.home_navigation_fragment, bundle)
}
}
override fun getLayoutResId() = R.layout.fragment_job_board
override fun initWidget() {
job_list_recycler_view.adapter = jobAdapter
back_to_main_image_view.setOnClickListener { onBackPressed() }
}
override fun observeLiveData() {
with(viewModel) {
jobList.observe(this#JobBoardFragment, Observer {
jobAdapter.submitList(it)
})
getInitialLoadingState().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is Progress -> {
if (state == loading(true)) {
network_loading_indicator.visible()
} else {
network_loading_indicator.visibilityGone()
}
}
is Failure -> context?.showToast(state.errorMessage.toString())
}
}
})
getPaginationState().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
when (state) {
is Progress -> {
if (state == loading(true)) {
pagination_loading_indicator.visible()
} else {
pagination_loading_indicator.visibilityGone()
}
}
is Failure -> context?.showToast(state.errorMessage.toString())
}
}
})
getTotalJob().observe(this#JobBoardFragment, Observer {
it.getContentIfNotHandled()?.let { state ->
job_board_text_view.visible()
with(profile_completed_image_view) {
visible()
text = state.toString()
}
}
})
}
}
}
But the problem is if data fetching failed due to internet connectivity or any other server related problem loading indicator does not invisible that means it still loading though I make the loadingStatus false and error message is shown. it means .doOnTerminate { initialLoadingState.postValue(Event(loading(false))) } is not called if error occured. This is the first problem. Another problem is loadInitial() and loadAfter() is being called simultaneously at the first call. But I just want the loadInitial() method is called at the beginning. after scrolling loadAfter() method will be called.
Try replacing all your LiveData's postValue() methods by setValue() or simply .value =.
The problem is that the postValue() method is for updating the value from a background thread to observers in the main thread. In this case you are always changing the values from the main thread itself, so you should use .value =.
Hope it's not too late.

How do I properly use IdeContentProposalProvider for xtext web editor

I am having trouble getting my proposal provider to work properly. My objective is to provide a list of all possible imports a user can create. I can verify that the class is bound properly and is getting called on assist request from the editor. I can even see proposals being created and passed to the acceptor but nothing shows up in the editor.
So my question may be two part:
Can you see what I may be doing wrong in the following code as to explain content assist not showing in the editor
Can you explain the difference between the dispatch methods for Assignment, RuleCall, and Keyword? I find that all three methods are hit for the same content assist request, so what exactly do I code differently and what do they do differently?
Also I am using CodeMirror if that has any effect.
class mydslContentAssist extends IdeContentProposalProvider{
#Inject extension mydslGrammarAccess stAccess
#Inject mydslGlobalScopeProvider sp
#Inject extension IQualifiedNameProvider
override dispatch createProposals(Assignment assignment, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) {
switch(assignment) {
case stAccess.libraryModelAccess.importsAssignment_0: {
var libs = sp.descriptionData.getExportedObjectsByType(ModelPackage.eINSTANCE.libraryModel)
for (lib : libs.filter[(EObjectOrProxy as LibraryModel).name.startsWith(context.prefix)]) {
var proposal = proposalCreator.createProposal('import ' + (lib.EObjectOrProxy as LibraryModel).name + ".*;", context) [
source = lib
description = "import entire library contents"
]
acceptor.accept(proposal, proposalPriorities.getDefaultPriority(proposal))
}
}
default : {
super._createProposals(assignment, context, acceptor)
}
}
}
override dispatch createProposals(RuleCall rulecall, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) {
switch(rulecall.rule) {
case importRule: {
var libs = sp.descriptionData.getExportedObjectsByType(ModelPackage.eINSTANCE.libraryModel)
for (lib : libs) {
var proposal = proposalCreator.createProposal('import ' + (lib.EObjectOrProxy as LibraryModel).name + ".*;", context) [
source = lib
description = "import entire library contents"
]
acceptor.accept(proposal, proposalPriorities.getDefaultPriority(proposal))
}
}
default : {
super._createProposals(rulecall, context, acceptor)
}
}
}
override dispatch createProposals(Keyword keyword, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) {
switch (keyword) {
case stAccess.importAccess.importKeyword_0: {
var libs = sp.descriptionData.getExportedObjectsByType(ModelPackage.eINSTANCE.libraryModel)
for (lib : libs) {
var proposal = proposalCreator.createProposal('import ' + (lib.EObjectOrProxy as LibraryModel).name + ".*;", context) [
source = lib
description = "import entire library contents"
]
acceptor.accept(proposal, proposalPriorities.getDefaultPriority(proposal))
}
}
default: {
super._createProposals(keyword, context, acceptor)
}
}
}
}
EDIT: My code is only being hit when calling content assist when typing the 'import' keyword. But not when typing the importedNamespace string
i gave it a try and could not reproduce
grammar:
Model:
imports+=Import*;
Import: 'import' importedNamespace=QualifiedNameWithWildcard ';'
;
QualifiedNameWithWildcard:
ID ("." ID)* (".*")?
;
and impl
package org.xtext.example.mydsl.web
import com.google.inject.Inject
import org.eclipse.xtext.Assignment
import org.eclipse.xtext.Keyword
import org.eclipse.xtext.RuleCall
import org.eclipse.xtext.ide.editor.contentassist.ContentAssistContext
import org.eclipse.xtext.ide.editor.contentassist.IIdeContentProposalAcceptor
import org.eclipse.xtext.ide.editor.contentassist.IdeContentProposalProvider
import org.eclipse.xtext.naming.IQualifiedNameProvider
import org.xtext.example.mydsl.services.MyDslGrammarAccess
class MydslContentAssist extends IdeContentProposalProvider {
#Inject extension MyDslGrammarAccess stAccess
static val LIBS = #[
"XX1", "XX2", "YY1", "YY2"
]
override dispatch createProposals(Assignment assignment, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) {
switch(assignment) {
case stAccess.importAccess.importedNamespaceAssignment_1: {
for (lib : LIBS.filter[it.startsWith(context.prefix)]) {
var proposal = proposalCreator.createProposal(lib + ".*;", context) [
source = lib
description = "import entire library contents"
]
acceptor.accept(proposal, proposalPriorities.getDefaultPriority(proposal))
}
}
default : {
super._createProposals(assignment, context, acceptor)
}
}
}
override dispatch createProposals(RuleCall rulecall, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) {
switch(rulecall.rule) {
case importRule: {
for (lib : LIBS) {
var proposal = proposalCreator.createProposal('import ' + lib + ".*;", context) [
source = lib
description = "import entire library contents"
]
acceptor.accept(proposal, proposalPriorities.getDefaultPriority(proposal))
}
}
default : {
super._createProposals(rulecall, context, acceptor)
}
}
}
override dispatch createProposals(Keyword keyword, ContentAssistContext context, IIdeContentProposalAcceptor acceptor) {
switch (keyword) {
case stAccess.importAccess.importKeyword_0: {
for (lib : LIBS) {
var proposal = proposalCreator.createProposal('import ' + lib + ".*;", context) [
source = lib
description = "import entire library contents"
]
acceptor.accept(proposal, proposalPriorities.getDefaultPriority(proposal))
}
}
default: {
super._createProposals(keyword, context, acceptor)
}
}
}
}

Conditional Iteration on a parsed json object using each in groovy

I'm trying to create an XML based on the data from a .json. So, my .json file looks something like:
{
"fruit1":
{
"name": "apple",
"quantity": "three",
"taste": "good",
"color": { "walmart": "{{red}}","tj": "{{green}}" }
},
"fruit2":
{
"name": "banana",
"quantity": "five",
"taste": "okay",
"color": { "walmart": "{{gmo}}","tj": "{{organic}}" }
}
}
I can create the XML just fine with the below code, from the above json
import groovy.xml.*
import groovy.json.JsonSlurper
def GenerateXML() {
def jsonSlurper = new JsonSlurper();
def fileReader = new BufferedReader(
new FileReader("/home/workspace/sample.json"))
def parsedData = jsonSlurper.parse(fileReader)
def writer = new FileWriter("sample.XML")
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
writer << builder.bind {
mkp.xmlDeclaration()
"friuts"(version:'$number', application: "FunApp"){
delegate.deployables {
parsedData.each { index, obj ->
"fruit"(name:obj.name, quantity:obj.quantity) {
delegate.taste(obj.taste)
delegate.color {
obj.color.each { name, value ->
it.entry(key:name, value)
}
}
}
}
}
}
}
}
I want to extend this code, such that it looks for particular keys. And if they are present, the loop is performed for those maps as well and as such extends the resulting file.
So, if i have the JSON as like so:
{"fruit1":
{
"name": "apple",
"quantity": "three",
"taste": "good",
"color": { "walmart": "{{red}}","tj": "{{green}}" }
},
"fruit2":
{
"name": "banana",
"quantity": "five",
"taste": "okay",
"color": { "walmart": "{{gmo}}","tj": "{{organic}}" }
},
"chip1":
{
"name": "lays",
"quantity": "one",
"type": "baked"
},
"chip2":
{
"name": "somename",
"quantity": "one",
"type": "fried"
}
}
I want to add an IF, so that it check if any key(s) like 'chip*' is there. And if yes, perform another iteration. If not just skip that section of logic, and not throw any err. like this
import groovy.xml.*
import groovy.json.JsonSlurper
def GenerateXML() {
def jsonSlurper = new JsonSlurper();
def fileReader = new BufferedReader(
new FileReader("/home/okram/workspace/objectsRepo/sample.json"))
def parsedData = jsonSlurper.parse(fileReader)
def writer = new FileWriter("sample.XML")
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
writer << builder.bind {
mkp.xmlDeclaration()
"fruits"(version:'$number', application: "FunApp"){
deployables {
parsedData.each { index, obj ->
"fruit"(name:obj.name, quantity:obj.quantity) {
taste(obj.taste)
color {
obj.color.each { name, value ->
it.entry(key:name, value)
}
}
}
}
}
}
if (parsedData.containsKey('chip*')){
//perform the iteration of the chip* maps
//to access the corresponding values
//below code fails, but that is the intent
parsedData.<onlyTheOnesPassing>.each { index1, obj1 ->
"Chips"(name:obj1.name, quantity:obj1.quantity) {
type(obj1.type)
}
}
}
}
}
I found the same dificult, but on Javascript language, if the logic help you, here what I made:
There are two ways:
You can use the library Lodash on the "get" here: Lodash get or the another one "has": Lodash has.
With they you can put the object and the path and check if there is one without getting any error.
Examples:
_.has(object, 'chip1.name');
// => false
_.has(object, 'fruit1');
// => true
Or you can put the code of the methods here:
// Recursively checks the nested properties of an object and returns the
// object property in case it exists.
static get(obj, key) {
return key.split(".").reduce(function (o, x) {
return (typeof o == "undefined" || o === null) ? o : o[x];
}, obj);
}
// Recursively checks the nested properties of an object and returns
//true in case it exists.
static has(obj, key) {
return key.split(".").every(function (x) {
if (typeof obj != "object" || obj === null || !x in obj)
return false;
obj = obj[x];
return true;
});
}
I hope it helps! :)

Resources