44package replacement
55
66import (
7+ "encoding/json"
78 "fmt"
89 "strings"
910
@@ -188,6 +189,14 @@ func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.Ta
188189 if selector .Options != nil && selector .Options .Create {
189190 createKind = value .YNode ().Kind
190191 }
192+
193+ // Check if this fieldPath contains structured data access
194+ if err := setValueInStructuredData (target , value , fp , createKind ); err == nil {
195+ // Successfully handled as structured data
196+ continue
197+ }
198+
199+ // Fall back to normal path handling
191200 targetFieldList , err := target .Pipe (& yaml.PathMatcher {
192201 Path : kyaml_utils .SmarterPathSplitter (fp , "." ),
193202 Create : createKind })
@@ -204,7 +213,7 @@ func copyValueToTarget(target *yaml.RNode, value *yaml.RNode, selector *types.Ta
204213
205214 for _ , t := range targetFields {
206215 if err := setFieldValue (selector .Options , t , value ); err != nil {
207- return err
216+ return fmt . Errorf ( "%w" , err )
208217 }
209218 }
210219 }
@@ -247,3 +256,146 @@ func setFieldValue(options *types.FieldOptions, targetField *yaml.RNode, value *
247256
248257 return nil
249258}
259+
260+ // setValueInStructuredData handles setting values within structured data (JSON/YAML) in scalar fields
261+ func setValueInStructuredData (target * yaml.RNode , value * yaml.RNode , fieldPath string , createKind yaml.Kind ) error {
262+ pathParts := kyaml_utils .SmarterPathSplitter (fieldPath , "." )
263+ if len (pathParts ) < 2 {
264+ return fmt .Errorf ("not a structured data path" )
265+ }
266+
267+ // Find the potential scalar field that might contain structured data
268+ var scalarFieldPath []string
269+ var structuredDataPath []string
270+ var foundScalar = false
271+
272+ // Try to find where the scalar field ends and structured data begins
273+ for i := 1 ; i <= len (pathParts ); i ++ {
274+ potentialScalarPath := pathParts [:i ]
275+ scalarField , err := target .Pipe (yaml .Lookup (potentialScalarPath ... ))
276+ if err != nil {
277+ continue
278+ }
279+ if scalarField != nil && scalarField .YNode ().Kind == yaml .ScalarNode && i < len (pathParts ) {
280+ // Try to parse the scalar value as structured data
281+ scalarValue := scalarField .YNode ().Value
282+ var parsedNode yaml.Node
283+ if err := yaml .Unmarshal ([]byte (scalarValue ), & parsedNode ); err == nil {
284+ // Successfully parsed - this is structured data
285+ scalarFieldPath = potentialScalarPath
286+ structuredDataPath = pathParts [i :]
287+ foundScalar = true
288+ break
289+ }
290+ }
291+ }
292+
293+ if ! foundScalar {
294+ return fmt .Errorf ("no structured data found in path" )
295+ }
296+
297+ // Get the scalar field containing structured data
298+ scalarField , err := target .Pipe (yaml .Lookup (scalarFieldPath ... ))
299+ if err != nil {
300+ return fmt .Errorf ("%w" , err )
301+ }
302+
303+ // Parse the structured data
304+ scalarValue := scalarField .YNode ().Value
305+ var parsedNode yaml.Node
306+ if err := yaml .Unmarshal ([]byte (scalarValue ), & parsedNode ); err != nil {
307+ return fmt .Errorf ("%w" , err )
308+ }
309+
310+ structuredData := yaml .NewRNode (& parsedNode )
311+
312+ // Navigate to the target location within the structured data
313+ targetInStructured , err := structuredData .Pipe (& yaml.PathMatcher {
314+ Path : structuredDataPath ,
315+ Create : createKind ,
316+ })
317+ if err != nil {
318+ return fmt .Errorf ("%w" , err )
319+ }
320+
321+ targetFields , err := targetInStructured .Elements ()
322+ if err != nil {
323+ return fmt .Errorf ("%w" , err )
324+ }
325+
326+ if len (targetFields ) == 0 {
327+ return fmt .Errorf ("unable to find field in structured data" )
328+ }
329+
330+ // Set the value in the structured data
331+ for _ , t := range targetFields {
332+ if t .YNode ().Kind == yaml .ScalarNode {
333+ t .YNode ().Value = value .YNode ().Value
334+ } else {
335+ t .SetYNode (value .YNode ())
336+ }
337+ }
338+
339+ // Serialize the modified structured data back to the scalar field
340+ // Try to detect if original was JSON or YAML and preserve formatting
341+ serializedData , err := serializeStructuredData (structuredData , scalarValue )
342+ if err != nil {
343+ return fmt .Errorf ("%w" , err )
344+ }
345+
346+ // Update the original scalar field
347+ scalarField .YNode ().Value = serializedData
348+
349+ return nil
350+ }
351+
352+ // serializeStructuredData handles the serialization of structured data back to string format
353+ // preserving the original format (JSON vs YAML) and style (pretty vs compact)
354+ func serializeStructuredData (structuredData * yaml.RNode , originalValue string ) (string , error ) {
355+ firstChar := rune (strings .TrimSpace (originalValue )[0 ])
356+ if firstChar == '{' || firstChar == '[' {
357+ return serializeAsJSON (structuredData , originalValue )
358+ }
359+
360+ // Fallback to YAML format
361+ return serializeAsYAML (structuredData )
362+ }
363+
364+ // serializeAsJSON converts structured data back to JSON format
365+ func serializeAsJSON (structuredData * yaml.RNode , originalValue string ) (string , error ) {
366+ modifiedData , err := structuredData .String ()
367+ if err != nil {
368+ return "" , fmt .Errorf ("failed to serialize structured data: %w" , err )
369+ }
370+
371+ // Parse the YAML output as JSON
372+ var jsonData interface {}
373+ if err := yaml .Unmarshal ([]byte (modifiedData ), & jsonData ); err != nil {
374+ return "" , fmt .Errorf ("failed to unmarshal YAML data: %w" , err )
375+ }
376+
377+ // Check if original was pretty-printed by looking for newlines and indentation
378+ if strings .Contains (originalValue , "\n " ) && strings .Contains (originalValue , " " ) {
379+ // Pretty-print the JSON to match original formatting
380+ if prettyJSON , err := json .MarshalIndent (jsonData , "" , " " ); err == nil {
381+ return string (prettyJSON ), nil
382+ }
383+ }
384+
385+ // Compact JSON
386+ if compactJSON , err := json .Marshal (jsonData ); err == nil {
387+ return string (compactJSON ), nil
388+ }
389+
390+ return "" , fmt .Errorf ("failed to marshal JSON data" )
391+ }
392+
393+ // serializeAsYAML converts structured data back to YAML format
394+ func serializeAsYAML (structuredData * yaml.RNode ) (string , error ) {
395+ modifiedData , err := structuredData .String ()
396+ if err != nil {
397+ return "" , fmt .Errorf ("failed to serialize YAML data: %w" , err )
398+ }
399+
400+ return strings .TrimSpace (modifiedData ), nil
401+ }
0 commit comments