Hur konverterar man URL-parametrar till ett JavaScript-objekt?


Jag har en sträng som denna:

abc=foo&def=%5Basf%5D&xyz=5

Hur kan jag konvertera det till ett JavaScript-objekt som detta?

{
  abc: 'foo',
  def: '[asf]',
  xyz: 5
}

Svar #1

År 2021... Vänligen betrakta detta som föråldrat.

Redigera

Denna redigering förbättrar och förklarar svaret baserat på kommentarerna.

var search = location.search.substring(1);
JSON.parse('{"' + decodeURI(search).replace(/"/g, '\\"').replace(/&/g, '","').replace(/=/g,'":"') + '"}')

Exempel

Analysera abc=foo&def=%5Basf%5D&xyz=5 i fem steg:

  • decodeURI: abc=foo&def=[asf]&xyz=5
  • Escape citattecken: samma, eftersom det inte finns några citattecken
  • Byt ut &: abc=foo","def=[asf]","xyz=5
  • Byt ut =: abc":"foo","def":"[asf]","xyz":"5
  • Omgiv med måsvingar och citattecken: {"abc":"foo","def":"[asf]","xyz":"5"}

vilket är laglig JSON.

En förbättrad lösning tillåter fler tecken i söksträngen. Den använder en återupplivningsfunktion för URI-avkodning:

var search = location.search.substring(1);
JSON.parse('{"' + search.replace(/&/g, '","').replace(/=/g,'":"') + '"}', function(key, value) { return key===""?value:decodeURIComponent(value) })

Exempel

search = "abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar";

ger

Object {abc: "foo", def: "[asf]", xyz: "5", foo: "b=ar"}

Originalt svar

En enradare:

JSON.parse('{"' + decodeURI("abc=foo&def=%5Basf%5D&xyz=5".replace(/&/g, "\",\"").replace(/=/g,"\":\"")) + '"}')

Svar #2

En rad. Ren och enkel.

const params = Object.fromEntries(new URLSearchParams(location.search));

För ditt specifika fall skulle det vara:

const str = 'abc=foo&def=%5Basf%5D&xyz=5';
const params = Object.fromEntries(new URLSearchParams(str));
console.log(params);

Svar #3

2023 ES6/7/8 och framåt tillvägagångssätt

Från och med ES6 erbjuder Javascript flera konstruktioner för att skapa en prestandaeffektiv lösning för detta problem.

Detta inkluderar att använda URLSearchParams och iteratorer

let params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
params.get("abc"); // "foo"

Om ditt användningsfall kräver att du faktiskt konverterar det till ett objekt, kan du implementera följande funktion:

function paramsToObject(entries) {
  const result = {}
  for(const [key, value] of entries) { // each 'entry' is a [key, value] tuple
    result[key] = value;
  }
  return result;
}

Grundläggande demo

const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const entries = urlParams.entries(); //returns an iterator of decoded [key,value] tuples
const params = paramsToObject(entries); //{abc:"foo",def:"[asf]",xyz:"5"}

Använda Object.fromEntries och spread

Vi kan använda Object.fromEntries och ersätta paramsToObject med Object.fromEntries(entries).

Värdeparen att iterera över är listans namn-värde-par där nyckeln är namnet och värdet är värdet.

Eftersom URLParams returnerar ett itererbart objekt, kommer användning av spridningsoperatorn istället för att anropa .entries också att ge poster enligt dess specifikation:

const urlParams = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5');
const params = Object.fromEntries(urlParams); // {abc: "foo", def: "[asf]", xyz: "5"}

Observera: Alla värden är automatiskt strängar enligt URLSearchParams-specifikationen

Flera samma nycklar

Som @siipe påpekade kommer strängar som innehåller flera värden med samma nyckel att tvingas till det sista tillgängliga värdet: foo=first_value&foo=second_value kommer i huvudsak att bli: {foo: "second_value"}.

Enligt detta svar: https://stackoverflow.com/a/1746566/1194694 finns det ingen specifikation för hur man ska hantera det och varje ramverk kan bete sig olika.

Ett vanligt användningsfall kommer att vara att slå ihop de två samma värdena i en array, vilket gör utdataobjektet till:

{foo: ["first_value", "second_value"]}

Detta kan uppnås med följande kod:

const groupParamsByKey = (params) => [...params.entries()].reduce((acc, tuple) => {
 // getting the key and value from each tuple
 const [key, val] = tuple;
 if(acc.hasOwnProperty(key)) {
    // if the current key is already an array, we'll add the value to it
    if(Array.isArray(acc[key])) {
      acc[key] = [...acc[key], val]
    } else {
      // if it's not an array, but contains a value, we'll convert it into an array
      // and add the current value to it
      acc[key] = [acc[key], val];
    }
 } else {
  // plain assignment if no special case is present
  acc[key] = val;
 }

return acc;
}, {});

const params = new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5&def=dude');
const output = groupParamsByKey(params) // {abc: "foo", def: ["[asf]", "dude"], xyz: 5}

Svar #4

2024 Enmeningsstrategi

För det allmänna fallet där du vill analysera förfrågningsparametrar till ett objekt:

Object.fromEntries(new URLSearchParams(location.search));

För ditt specifika fall:

Object.fromEntries(new URLSearchParams('abc=foo&def=%5Basf%5D&xyz=5'));

TypeScript-lösning för att analysera upprepade (array) query-parametrar:

type QueryParams = Record<string, string | string[]>;

const parseQueryParams = (url: string): QueryParams => 
  Array.from(new URL(url).searchParams.entries()).reduce((q, [key, value]) => 
    ({
      ...q,
      [key]: q[key]
        ? Array.isArray(q[key])
          ? [...q[key], value]
          : [q[key] as string, value]
        : value,
    }), {} as QueryParams);

Svar #5

Dela på & för att få namn-/värdepar, dela sedan varje par på =. Här är ett exempel:

var str = "abc=foo&def=%5Basf%5D&xy%5Bz=5"
var obj = str.split("&").reduce(function(prev, curr, i, arr) {
    var p = curr.split("=");
    prev[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
    return prev;
}, {});

Ett annat tillvägagångssätt, med reguljära uttryck:

var obj = {}; 
str.replace(/([^=&]+)=([^&]*)/g, function(m, key, value) {
    obj[decodeURIComponent(key)] = decodeURIComponent(value);
}); 

Detta är anpassat från John Resigs "Search and Don’t Replace".

Svar #6

De föreslagna lösningarna jag har hittat hittills täcker inte mer komplexa scenarier.

Jag behövde konvertera en frågesträng som

https://random.url.com?Target=Offer&Method=findAll&filters%5Bhas_goals_enabled%5D%5BTRUE%5D=1&filters%5Bstatus%5D=active&fields%5B%5D=id&fields%5B%5D=name&fields%5B%5D=default_goal_name

till ett objekt som:

{
    "Target": "Offer",
    "Method": "findAll",
    "fields": [
        "id",
        "name",
        "default_goal_name"
    ],
    "filters": {
        "has_goals_enabled": {
            "TRUE": "1"
        },
        "status": "active"
    }
}

ELLER :

https://random.url.com?Target=Report&Method=getStats&fields%5B%5D=Offer.name&fields%5B%5D=Advertiser.company&fields%5B%5D=Stat.clicks&fields%5B%5D=Stat.conversions&fields%5B%5D=Stat.cpa&fields%5B%5D=Stat.payout&fields%5B%5D=Stat.date&fields%5B%5D=Stat.offer_id&fields%5B%5D=Affiliate.company&groups%5B%5D=Stat.offer_id&groups%5B%5D=Stat.date&filters%5BStat.affiliate_id%5D%5Bconditional%5D=EQUAL_TO&filters%5BStat.affiliate_id%5D%5Bvalues%5D=1831&limit=9999

IN I :

{
    "Target": "Report",
    "Method": "getStats",
    "fields": [
        "Offer.name",
        "Advertiser.company",
        "Stat.clicks",
        "Stat.conversions",
        "Stat.cpa",
        "Stat.payout",
        "Stat.date",
        "Stat.offer_id",
        "Affiliate.company"
    ],
    "groups": [
        "Stat.offer_id",
        "Stat.date"
    ],
    "limit": "9999",
    "filters": {
        "Stat.affiliate_id": {
            "conditional": "EQUAL_TO",
            "values": "1831"
        }
    }
}

Jag sammanställde och anpassade flera lösningar till en som faktiskt fungerar:

KOD :

var getParamsAsObject = function (query) {

    query = query.substring(query.indexOf('?') + 1);

    var re = /([^&=]+)=?([^&]*)/g;
    var decodeRE = /\+/g;

    var decode = function (str) {
        return decodeURIComponent(str.replace(decodeRE, " "));
    };

    var params = {}, e;
    while (e = re.exec(query)) {
        var k = decode(e[1]), v = decode(e[2]);
        if (k.substring(k.length - 2) === '[]') {
            k = k.substring(0, k.length - 2);
            (params[k] || (params[k] = [])).push(v);
        }
        else params[k] = v;
    }

    var assign = function (obj, keyPath, value) {
        var lastKeyIndex = keyPath.length - 1;
        for (var i = 0; i < lastKeyIndex; ++i) {
            var key = keyPath[i];
            if (!(key in obj))
                obj[key] = {}
            obj = obj[key];
        }
        obj[keyPath[lastKeyIndex]] = value;
    }

    for (var prop in params) {
        var structure = prop.split('[');
        if (structure.length > 1) {
            var levels = [];
            structure.forEach(function (item, i) {
                var key = item.replace(/[?[\]\\ ]/g, '');
                levels.push(key);
            });
            assign(params, levels, params[prop]);
            delete(params[prop]);
        }
    }
    return params;
};

Svar #7

En kortfattad lösning:

location.search
  .slice(1)
  .split('&')
  .map(p => p.split('='))
  .reduce((obj, pair) => {
    const [key, value] = pair.map(decodeURIComponent);
    obj[key] = value;
    return obj;
  }, {});

Svar #8

Detta är den enkla versionen, uppenbarligen kommer du vilja lägga till någon felkontroll:

var obj = {};
var pairs = queryString.split('&');
for(i in pairs){
    var split = pairs[i].split('=');
    obj[decodeURIComponent(split[0])] = decodeURIComponent(split[1]);
}

Svar #9

För Node JS kan du använda Node JS API querystring:

const querystring = require('querystring');

querystring.parse('abc=foo&def=%5Basf%5D&xyz=5&foo=b%3Dar');
// returns the object

Dokumentation: https://nodejs.org/api/querystring.html

Svar #10

Jag tyckte att $.String.deparam var den mest kompletta färdiga lösningen (kan hantera nästlade objekt osv.). Kolla dokumentationen.

Svar #11

Det finns ett ganska enkelt och felaktigt svar med ES6:

console.log(
  Object.fromEntries(new URLSearchParams(`abc=foo&def=%5Basf%5D&xyz=5`))
);

Men denna enkla rad kod täcker inte flera samma nycklar, du måste använda något mer komplicerat:

function parseParams(params) {
  const output = [];
  const searchParams = new URLSearchParams(params);

  // Set will return only unique keys()
  new Set([...searchParams.keys()])
    .forEach(key => {
      output[key] = searchParams.getAll(key).length > 1 ?  
        searchParams.getAll(key) : // get multiple values
        searchParams.get(key); // get single value
    });

  return output;
}

console.log(
   parseParams('abc=foo&cars=Ford&cars=BMW&cars=Skoda&cars=Mercedes')
)

Koden kommer att generera följande struktur:

[
  abc: "foo"
  cars: ["Ford", "BMW", "Skoda", "Mercedes"]
]

Relaterade artiklar