2

I'm having difficulty generating a json object in the below format from the data below. Fiddle

[ { Invoice: 
    { 
       headers: { date: "15-01-2020", buyer: "McDonalds", order: "145632"}, 
       items: { name: "Big Mac",   quantity: "5",  rate: "20.00"}, 
       items: { name: "Small Mac", quantity: "10", rate: "10.00"} 
    } 
  } 
, { Invoice: { // Other invoices go here, I've used just one for this example} } 
] 
<div class="invoice">
  <div class="header">
    <div contenteditable data="date">15-Jan-2020</div>
    <div contenteditable  data="buyer">McDonalds</div>
    <div contenteditable  data="order">145632</div>
  </div>
  <div class="item">
    <div contenteditable data="name">Big Mac</div>
    <div contenteditable data="quantity">5</div>
    <div contenteditable data="rate">20.00</div>
  </div>
  <div class="item">
    <div contenteditable data="name">Small Mac</div>
    <div contenteditable data="quantity">10</div>
    <div contenteditable data="rate">10.00</div>
  </div>
</div>

<button>Loop</button>

jQuery

var button = $("button")
button.on("click",function() {  
  jsonObj =[];
  $('.invoice>.header>div, .invoice>.item>div').each(function(index,item) {
    console.log($(this).parent().attr('class'));
    console.log($(this).attr('data'),$(this).text());
    q = {}
    q ['header'] = $(this).parent().attr('class');
    q [$(this).attr('data')] = $(this).text();

    jsonObj.push(q);
   });
   console.log(jsonObj);
   console.log(JSON.stringify(jsonObj));
});

I current end up with an object like this where the keys are repeated everywhere. How can I get this right?

  [ { "header": "header", "date": "15-Jan-2020"} 
  , { "header": "header", "buyer": "McDonalds"} 
  , { "header": "header", "order": "145632"} 
  , { "header": "item", "name": "Big Mac"} 
  , { "header": "item", "quantity": "5"} 
  , { "header": "item", "rate": "20.00"} 
  , { "header": "item", "name": "Small Mac"} 
  , { "header": "item", "quantity": "10"} 
  , { "header": "item", "rate": "10.00"} 
  ] 
5
  • 2
    you can repeat the name of a key 1000 times in a json but only the last one is valid, the others are all canceled by their next Commented Jan 19, 2020 at 2:56
  • I may have an answer, but it is in JS not in jQuery... Commented Jan 19, 2020 at 3:15
  • No problem if it's in JS. Commented Jan 19, 2020 at 3:42
  • Ok. I'm still working on all of this. Commented Jan 19, 2020 at 5:34
  • I made again some new changes to my double answer, please have a look? ;) Commented Jan 20, 2020 at 21:56

2 Answers 2

2

In your example, you have an object with two same keys:

"items":{
    "name":"Big Mac",
    "quantity":"5",
    "rate":"20.00"
}
"items":{
    "name":"Small Mac",
    "quantity":"10",
    "rate":"10.00"
}

This won't work, because you can only have one, so you need to change the value of items key to an array of objects:

"items":[
    {
        "name":"Big Mac",
        "quantity":"5",
        "rate":"20.00"
    },
    {
        "name":"Small Mac",
        "quantity":"10",
        "rate":"10.00"
    }
]

The iteration code may look like this:

const jsonObj = [];

$('.invoice').each((index, item) => {
 const invoice = {
    header: {},
    items: []
 };

 $(item).find('.header > div').each((index, item) => {
    const key = $(item).attr('data');
    invoice.header[key] = $(item).text();
 });

 $(item).find('.item').each((index, item) => {
    const itemObj = {};

    $(item).find('div').each((index, item) => {
        const key = $(item).attr('data');
        itemObj[key] = $(item).text();
    });

    invoice.items.push(itemObj);
 });

 jsonObj.push({
    Invoice: invoice
 });
});

The main difference from your version is that it iterates through the dom step by step. First, through each invoice, then through each header of the invoice and each item. This way it's easy to build the desired structure.

Here's the jsfiddle link: https://jsfiddle.net/tara5/tanb174h/

Sign up to request clarification or add additional context in comments.

Comments

2

Pure JS code for this:

Remarks : I have changed:
<div ... data="..."> ... </div>
to
<div ... data-ref="..."> ... </div>

to be conform with HTML directives (see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-* )

const jsonObj  = []
  ,   inVoices = document.querySelector('.invoice')
  ,   subDivOf = (parent,query) => [...parent.querySelectorAll(query)]
  ,   dataElms = (ac,el)=>{ac[el.dataset.ref]=el.textContent;return ac }
  ;

Loop.onclick=_=>
  {
  jsonObj.push( { Invoice: getInVoicesValues() } )
  console.clear()
  console.log( jsonObj )
  }
function getInVoicesValues() 
  {
  const headers  = subDivOf(inVoices,'.header>div').reduce(dataElms,{})
    ,   items    = subDivOf(inVoices,'.item').reduce((accI,itm)=>
                      {
                      accI.push( subDivOf(itm, 'div').reduce(dataElms,{}))
                      return accI
                      },[])
      ;
  return { headers, items }      
  }
.as-console-wrapper {
    max-height: 100% !important;
    width: 70% !important;
    top: 0; left: 30% !important;
  }
  div.invoice>div:before {
    display:  block;
    content: attr(class) ' :';
  }
  div[contenteditable] {
    font: 12px Arial, Helvetica, sans-serif;
    margin: .3em;
    width: 12em;
    border : 1px solid grey; 
    padding:.2em 1em;
    margin-left:6em;
  }
  div[contenteditable]::before {
    display: inline-block;
    content: attr(data-ref);
    font-weight : bold;
    width: 4.9em;
    margin-left:-5.3em;
  }
<div class="invoice">
  <div class="header">
    <div contenteditable data-ref="date">15-Jan-2020</div>
    <div contenteditable data-ref="buyer">McDonalds</div>
    <div contenteditable data-ref="order">145632</div>
  </div>
  <div class="item">
    <div contenteditable data-ref="name">Big Mac</div>
    <div contenteditable data-ref="quantity">5</div>
    <div contenteditable data-ref="rate">20.00</div>
  </div>
  <div class="item">
    <div contenteditable data-ref="name">Small Mac</div>
    <div contenteditable data-ref="quantity">10</div>
    <div contenteditable data-ref="rate">10.00</div>
  </div>
</div>
    
<button id="Loop">Loop</button>

.........................run snippets full screen for better view

second method

const jsonObj      = [];
const inVoicesElms = document.querySelectorAll('.invoice div');

Loop.onclick=_=>
  {
  jsonObj.push( { Invoice: getInVoicesValues() } )
  console.clear()
  console.log( jsonObj )
  }
function getInVoicesValues() 
  {
  let rep  = { headers:{}, items:[] }
    , cur  = null
    ;
  inVoicesElms.forEach(el =>
    {
    if (el.matches('.header'))
      {
      cur = rep.headers
      }
    else if (el.matches('.item'))
      {
      cur = {}
      rep.items.push(cur)
      }
    else // (el.matches('[contenteditable]'))
      {
      cur[el.getAttribute('data')] = el.textContent
      }
    })
  return rep
  }
.as-console-wrapper { max-height: 100% !important; width: 70% !important;
    top: 0; left: 30% !important;
  }
<div class="invoice">
  <div class="header">
    <div contenteditable data="date">15-Jan-2020</div>
    <div contenteditable data="buyer">McDonalds</div>
    <div contenteditable data="order">145632</div>
  </div>
  <div class="item">
    <div contenteditable data="name">Big Mac</div>
    <div contenteditable data="quantity">5</div>
    <div contenteditable data="rate">20.00</div>
  </div>
  <div class="item">
    <div contenteditable data="name">Small Mac</div>
    <div contenteditable data="quantity">10</div>
    <div contenteditable data="rate">10.00</div>
  </div>
</div>
    
<button id="Loop">Loop</button>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.