@@ -146,37 +146,57 @@ export class JsonFile {
146146 */
147147 // tslint:disable-next-line:no-any
148148 public static validateNoUndefinedMembers ( jsonObject : Object ) : void {
149- return JsonFile . _validateNoUndefinedMembers ( jsonObject , '' ) ;
149+ return JsonFile . _validateNoUndefinedMembers ( jsonObject , [ ] ) ;
150150 }
151151
152152 // Private implementation of validateNoUndefinedMembers()
153- private static _validateNoUndefinedMembers ( jsonObject : Object , path : string ) : void {
153+ private static _validateNoUndefinedMembers ( jsonObject : Object , keyPath : string [ ] ) : void {
154154 if ( ! jsonObject ) {
155155 return ;
156156 }
157157 if ( typeof jsonObject === 'object' ) {
158158 for ( const key of Object . keys ( jsonObject ) ) {
159- let fullPath : string = path ;
160- if ( Array . isArray ( jsonObject ) ) {
161- fullPath += `[${ key } ]` ;
162- } else if ( / ^ [ a - z _ 0 - 9 ] + $ / i. test ( key ) ) {
163- if ( fullPath ) {
164- fullPath += '.' ;
165- }
166- fullPath += `${ key } ` ;
167- } else {
168- fullPath += `["${ key . replace ( / [ " ] / g, '\\"' ) } "]` ;
169- }
159+ keyPath . push ( key ) ;
170160
171161 // tslint:disable-next-line:no-any
172162 const value : any = jsonObject [ key ] ;
173163 if ( value === undefined ) {
164+ const fullPath : string = JsonFile . _formatKeyPath ( keyPath ) ;
174165 throw new Error ( `The value for ${ fullPath } is undefined` ) ;
175166 }
176167
177- JsonFile . _validateNoUndefinedMembers ( value , fullPath ) ;
168+ JsonFile . _validateNoUndefinedMembers ( value , keyPath ) ;
169+ keyPath . pop ( ) ;
170+ }
171+ }
172+ }
173+
174+ // Given this input: ['items', '4', 'syntax', 'parameters', 'string "with" symbols", 'type']
175+ // Return this string: items[4].syntax.parameters["string \"with\" symbols"].type
176+ private static _formatKeyPath ( keyPath : string [ ] ) : string {
177+ let result : string = '' ;
178+
179+ for ( const key of keyPath ) {
180+ if ( / ^ [ 0 - 9 ] + $ / . test ( key ) ) {
181+ // It's an integer, so display like this: parent[123]
182+ result += `[${ key } ]` ;
183+ } else if ( / ^ [ a - z _ ] [ a - z _ 0 - 9 ] * $ / i. test ( key ) ) {
184+ // It's an alphanumeric identifier, so display like this: parent.name
185+ if ( result ) {
186+ result += '.' ;
187+ }
188+ result += `${ key } ` ;
189+ } else {
190+ // It's a freeform string, so display like this: parent["A path: \"C:\\file\""]
191+
192+ // Convert this: A path: "C:\file"
193+ // To this: A path: \"C:\\file\"
194+ const escapedKey : string = key . replace ( / [ \\ ] / g, '\\\\' ) // escape backslashes
195+ . replace ( / [ " ] / g, '\\' ) ; // escape quotes
196+ result += `["${ escapedKey } "]` ;
178197 }
179198 }
199+ return result ;
180200 }
181201
182202 /**
0 commit comments