-1

I have a form and I am using Ajax to submit data to the API. The form contains a repeater which aids the user to enter any number of data repeatedly. The user submits the form after a few steps. When I try to bind the value to the Object is not binding. The API is built using Spring Boot, and by default, Jackson is used for conversion of JSON to Objects.

Here is the full code,

f.submit(function(e){
            e.preventDefault();
            var ignoreFields = ["_csrf"];
            var unindexed_array = $(this).serializeArray().filter(val => ignoreFields.indexOf(val.name) === -1);
            var indexed_array = {};
            $.map(unindexed_array, function(n, i){
                indexed_array[n['name']] = n['value'];
            });
            $.ajax({
                  type: $(this).attr('method'), 
                  contentType: 'application/json',
                  url: $(this).attr('action'), 
                  data: JSON.stringify(indexed_array),
                  dataType: 'json', cache: false, timeout: 600000,
                  success: function (d) {
                  }, error: function (e) {
                  }
              });
        })

before JSON.stringify()

   {
    act1[0].quantity: "4",
    act1[0].name: "abc",
    act1[0].status: "Working",
    act2[0].quantity: "5",
    act2[0].id: "1",
    act3[0].quantity: "4",
    act3[0].id: "2",
    act3[0].unit: "mm",
    activity: "zdzdsd zsdsad",
    endTime: "23:02",
    location: "sdasd",
    note: "sdfsdfsd sdfsdfsd",
    startTime: "03:03"
  }

if I convert the above code to the following format I expect this to work, since I am using a list and Jackson Library will be able to identify this format

    {
    endTime: "23:02",
    location: "sdasd",
    note: "sdfsdfsd sdfsdfsd",
    startTime: "03:03",
    act1:[{
       quantity: "4",
       name: "abc",
       status: "Working"
    }],
    act2:[{
       quantity: "5"
       id: "1"
    }],
    act3:[{
       quantity: "4"
       id: "2",
       unit: "mm"
    }]
}

I tried many ways in Javascript to convert into this format, How can we achieve the above format?

Sebin Thomas
  • 279
  • 8
  • 21
  • It's a bit unclear what you're trying to do here. I'm not sure what is your input and what is your output. And neither sample is a legal JS representation of an object (although the latter would be if wrapped in `{` - `}`. What are you trying to convert into what? And can you show us some of what you've tried? – Scott Sauyet Sep 21 '20 at 15:27
  • @ScottSauyet I have modified my Question, which I do not know whether you can understand, I really do not know how to ask this, its a format issue, the latter you see is the format I need and the first one is the format I have. How can I convert the first JSON to the last JSON format? – Sebin Thomas Sep 21 '20 at 15:40
  • 1
    How do you get that input? Is it a multi-line string as shown? Because it is still not a legal JS format. An object key can have pretty well any characters, but in source code or in JSON, anything beyond alphanumeric characters /$ /_ are disallowed unless wrapped in a String. That is, this is not legal: `{act1[0].quantity: "4"}`, but this (with the key wrapped in a string) is legal: `{"act1[0].quantity": "4"}`. – Scott Sauyet Sep 21 '20 at 15:59

1 Answers1

1

Keeping a personal library of helpful functions can often make this sort of transformation easier. I have used functions like the included assocPath, and hydrate many times, including in a recent SO answer (which has more details about what they do.)

Put together my solution involves a number of fairly small functions. I do have to make an assumption about your input structure not entirely clear from the question, namely that what's displayed is some kind of shorthand for a real JS format. If not, most of the code will probably still work; it just might take some additional effort to convert to something usable.

This is what it looks like:

// utility functions
const assoc = (prop, val, obj) => 
  Number .isInteger (prop) && Array .isArray (obj)
    ? [... obj .slice (0, prop), val, ...obj .slice (prop + 1)]
    : {...obj, [prop]: val}

const assocPath = ([p = undefined, ...ps], val, obj) => 
  p == undefined
    ? obj
    : ps.length == 0
      ? assoc(p, val, obj)
      : assoc(p, assocPath(ps, val, obj[p] || (obj[p] = Number .isInteger (ps[0]) ? [] : {})), obj)

const hydrate = (pvEntries) =>
  pvEntries .reduce ((a, [k, v]) => assocPath (k, v, a), {})


// helper functions
const extractIndices = s => 
  s .split (/[\[\]]/)
    .map (s => /^\d+$/ .test (s) ? Number(s) : s)
    .filter(n => n !== '')

const makePath = (s) => 
  s .split ('.') .flatMap (extractIndices)


// main function
const transform = (input) =>
  hydrate (
    Object .entries (input)
      .map (([k, v]) => [makePath (k), v])
  )


// test data
const input = {"act1[0].quantity": "4", "act1[0].name": "abc", "act1[0].status": "Working", "act2[0].quantity": "5", "act2[0].id": "1", "act3[0].quantity": "4", "act3[0].id": "2", "act3[0].unit": "mm", "activity": "zdzdsd zsdsad", "endTime": "23:02", "location": "sdasd", "note": "sdfsdfsd sdfsdfsd", "startTime": "03:03"}


// demo
console .log (transform (input))
.as-console-wrapper {max-height: 100% !important; top: 0}

Using the helpers I already had, the only trick I needed in order to build this is to write some code to transform this style input: "act1[0].quantity" into the style I prefer to use, ["act1", 0, "quantity"]. That code is makePath, which depends on extractIndices. While they could easily be combined here, extractIndices looks as though it might be useful on its own.

Using that, we can transform your input above into this:

[
  [["act1", 0, "quantity"], "4"],
  [["act1", 0, "name"], "abc"],
  [["act1", 0, "status"], "Working"],
  [["act2", 0, "quantity"], "5"],
  [["act2", 0, "id"], "1"],
  [["act3", 0, "quantity"], "4"],
  [["act3", 0, "id"], "2"],
  [["act3", 0, "unit"], "mm"],
  [["activity"], "zdzdsd zsdsad"],
  [["endTime"], "23:02"],
  [["location"], "sdasd"],
  [["note"], "sdfsdfsd sdfsdfsd"],
  [["startTime"], "03:03"]
]

and then simply call hydrate on that result.

Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • You saved my day. Thank you very much. – Sebin Thomas Sep 22 '20 at 01:29
  • 1
    @SebrinThomas: I'm glad it helped, but I hope you take away more than the code. Much more important is the notion of building and reusing many simple functions in order to build your more complex ones. – Scott Sauyet Sep 22 '20 at 01:45