2013-03-09 19 views
11

Jeśli mam zserializowaną JSON z json.net tak:Resolve okrągłe referencje od obiektu JSON

User:{id:1,{Foo{id:1,prop:1}}, 
FooList{$ref: "1",Foo{id:2,prop:13}} 

chcę mieć wyjście nokaut foreach nad FooList ale nie jestem pewien, jak postępować ponieważ $ ref rzeczy mogą rzucić rzeczy.

myślę rozwiązanie byłoby jakoś zmusić cały Foos mają być świadczone w FooList nie używając:

PreserveReferencesHandling = PreserveReferencesHandling.Objects 

ale wydaje marnotrawstwem ..

+0

Inne rozwiązanie to: http://stackoverflow.com/questions/10747341/is-there-a-jquery-function-that-can-take-a-ref- id-value-from-a-parse-json-stri/12622112 # 12622112 – jvitor83

+0

Zobacz także [JsonNetDecycle] (https://bitbucket.org/smithkl42/jsonnetdecycle) –

Odpowiedz

13

Obiekt json którego odbiera z serwera zawiera Circular References. Przed użyciem obiektu powinieneś najpierw usunąć wszystkie właściwości $ref z obiektu, czyli w miejsce $ref : "1" musisz umieścić obiekt, który to łącze wskazuje.

W twoim przypadku może być ona skierowana do obiektu użytkownika, którego identyfikator jest 1

W tym celu należy sprawdzić Douglas Crockfords Plugin on github .There jest cycle.js co może zrobić dla ciebie zadanie.

lub można użyć następującego kodu (nie sprawdzone)

function resolveReferences(json) { 
    if (typeof json === 'string') 
     json = JSON.parse(json); 

    var byid = {}, // all objects by id 
     refs = []; // references to objects that could not be resolved 
    json = (function recurse(obj, prop, parent) { 
     if (typeof obj !== 'object' || !obj) // a primitive value 
      return obj; 
     if ("$ref" in obj) { // a reference 
      var ref = obj.$ref; 
      if (ref in byid) 
       return byid[ref]; 
      // else we have to make it lazy: 
      refs.push([parent, prop, ref]); 
      return; 
     } else if ("$id" in obj) { 
      var id = obj.$id; 
      delete obj.$id; 
      if ("$values" in obj) // an array 
       obj = obj.$values.map(recurse); 
      else // a plain object 
       for (var prop in obj) 
        obj[prop] = recurse(obj[prop], prop, obj) 
      byid[id] = obj; 
     } 
     return obj; 
    })(json); // run it! 

    for (var i=0; i<refs.length; i++) { // resolve previously unknown references 
     var ref = refs[i]; 
     ref[0][ref[1]] = byid[refs[2]]; 
     // Notice that this throws if you put in a reference at top-level 
    } 
    return json; 
} 

Daj mi znać, jeśli to pomaga!

+0

wow, niesamowite rzeczy !! – FutuToad

+0

Jeśli przeniesiesz byid [id] = obj assignment (za zmienną id = ...), otrzymasz znacznie mniej wpisów w tablicy refs. Na wykresach obiektów nie uzyskałem żadnego. – Rolf

23

znalazłem kilka błędów i wdrażane tablice wsparcia:

function resolveReferences(json) { 
    if (typeof json === 'string') 
     json = JSON.parse(json); 

    var byid = {}, // all objects by id 
     refs = []; // references to objects that could not be resolved 
    json = (function recurse(obj, prop, parent) { 
     if (typeof obj !== 'object' || !obj) // a primitive value 
      return obj; 
     if (Object.prototype.toString.call(obj) === '[object Array]') { 
      for (var i = 0; i < obj.length; i++) 
       // check also if the array element is not a primitive value 
       if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value 
        continue; 
       else if ("$ref" in obj[i]) 
        obj[i] = recurse(obj[i], i, obj); 
       else 
        obj[i] = recurse(obj[i], prop, obj); 
      return obj; 
     } 
     if ("$ref" in obj) { // a reference 
      var ref = obj.$ref; 
      if (ref in byid) 
       return byid[ref]; 
      // else we have to make it lazy: 
      refs.push([parent, prop, ref]); 
      return; 
     } else if ("$id" in obj) { 
      var id = obj.$id; 
      delete obj.$id; 
      if ("$values" in obj) // an array 
       obj = obj.$values.map(recurse); 
      else // a plain object 
       for (var prop in obj) 
        obj[prop] = recurse(obj[prop], prop, obj); 
      byid[id] = obj; 
     } 
     return obj; 
    })(json); // run it! 

    for (var i = 0; i < refs.length; i++) { // resolve previously unknown references 
     var ref = refs[i]; 
     ref[0][ref[1]] = byid[ref[2]]; 
     // Notice that this throws if you put in a reference at top-level 
    } 
    return json; 
} 
+0

dziękuję, możesz wyjaśnić więcej, co zmieniłeś? – FutuToad

+1

- Dodano if (Object.prototype.toString.call (obj) === '[Array obiektu]) {...} - Niemal najnowszy ciąg był z błędem: ref [0] [ref [1 ]] = byid [odniesienia [2]]; , ale musi być: ref [0] [ref [1]] = byid [ref [2]]; - I ten ciąg był: obj [prop] = recurse (obj [prop], prop, obj) i staje się: obj [prop] = recurse (obj [prop], prop, obj); –

+1

wielkie dzięki! Spędzam dużo czasu na poszukiwaniu błędów! –

3

miałem problemy z korektą tablicy w odpowiedzi na Aleksandr Wasiljew.

Nie mogę skomentować jego odpowiedzi (nie mam wystarczającej liczby punktów reputacji ;-)), więc musiałem dodać nową odpowiedź ... (gdzie miałem wyskakujące okienko jako najlepszą praktykę, aby nie odpowiadać na inne odpowiedzi i tylko na oryginalne pytanie - bOF)

if (Object.prototype.toString.call(obj) === '[object Array]') { 
     for (var i = 0; i < obj.length; i++) { 
      // check also if the array element is not a primitive value 
      if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value 
       return obj[i]; 
      if ("$ref" in obj[i]) 
       obj[i] = recurse(obj[i], i, obj); 
      else 
       obj[i] = recurse(obj[i], prop, obj); 
     } 
     return obj; 
    } 
+0

Zmieniam kod w moim poście. Dziękuję Ci! : D –

+1

Jednak nie używam go już w produkcji, ponieważ najnowsza wersja Microsoft ASP.NET Implementacja po stronie serwera ODATA nie obsługuje danych wyjściowych z "$ ref", aby odwoływać się do już zwróconych obiektów. Microsoft twierdzi na forach, że nie będą go wdrażać. ;-( –

+0

Czy konieczne jest wprowadzenie rozróżnienia $ ref w pętli tablicowej? Następna instancja zrobi to i tak, a w przypadku braku ref, wątpię, aby przekazanie "prop" jako drugiego parametru było poprawne – Rolf

2

w przyjętym realizacji, jeśli inspekcji tablicę i natknąć się na prymitywnej wartości, to zwróci tę wartość i zastąpić tę tablicę. Zamiast tego chcesz kontynuować sprawdzanie wszystkich elementów tablicy i zwróć tablicę na końcu.

function resolveReferences(json) { 
    if (typeof json === 'string') 
     json = JSON.parse(json); 

    var byid = {}, // all objects by id 
     refs = []; // references to objects that could not be resolved 
    json = (function recurse(obj, prop, parent) { 
     if (typeof obj !== 'object' || !obj) // a primitive value 
      return obj; 
     if (Object.prototype.toString.call(obj) === '[object Array]') { 
      for (var i = 0; i < obj.length; i++) 
       // check also if the array element is not a primitive value 
       if (typeof obj[i] !== 'object' || !obj[i]) // a primitive value 
        continue; 
       else if ("$ref" in obj[i]) 
        obj[i] = recurse(obj[i], i, obj); 
       else 
        obj[i] = recurse(obj[i], prop, obj); 
      return obj; 
     } 
     if ("$ref" in obj) { // a reference 
      var ref = obj.$ref; 
      if (ref in byid) 
       return byid[ref]; 
      // else we have to make it lazy: 
      refs.push([parent, prop, ref]); 
      return; 
     } else if ("$id" in obj) { 
      var id = obj.$id; 
      delete obj.$id; 
      if ("$values" in obj) // an array 
       obj = obj.$values.map(recurse); 
      else // a plain object 
       for (var prop in obj) 
        obj[prop] = recurse(obj[prop], prop, obj); 
      byid[id] = obj; 
     } 
     return obj; 
    })(json); // run it! 

    for (var i = 0; i < refs.length; i++) { // resolve previously unknown references 
     var ref = refs[i]; 
     ref[0][ref[1]] = byid[ref[2]]; 
     // Notice that this throws if you put in a reference at top-level 
    } 
    return json; 
} 
0

moje rozwiązanie (działa dla tablic, jak również):

Wykorzystanie: rebuildJsonDotNetObj (jsonDotNetResponse)

Kod:

function rebuildJsonDotNetObj(obj) { 
    var arr = []; 
    buildRefArray(obj, arr); 
    return setReferences(obj, arr) 
} 

function buildRefArray(obj, arr) { 
    if (!obj || obj['$ref']) 
     return; 
    var objId = obj['$id']; 
    if (!objId) 
    { 
     obj['$id'] = "x"; 
     return; 
    } 
    var id = parseInt(objId); 
    var array = obj['$values']; 
    if (array && Array.isArray(array)) { 
     arr[id] = array; 
     array.forEach(function (elem) { 
      if (typeof elem === "object") 
       buildRefArray(elem, arr); 
     }); 
    } 
    else { 
     arr[id] = obj; 
     for (var prop in obj) { 
      if (typeof obj[prop] === "object") { 
       buildRefArray(obj[prop], arr); 
      } 
     } 
    } 
} 

function setReferences(obj, arrRefs) { 
    if (!obj) 
     return obj; 
    var ref = obj['$ref']; 
    if (ref) 
     return arrRefs[parseInt(ref)]; 

    if (!obj['$id']) //already visited 
     return obj; 

    var array = obj['$values']; 
    if (array && Array.isArray(array)) { 
     for (var i = 0; i < array.length; ++i) 
      array[i] = setReferences(array[i], arrRefs) 
     return array; 
    } 
    for (var prop in obj) 
     if (typeof obj[prop] === "object") 
      obj[prop] = setReferences(obj[prop], arrRefs) 
    delete obj['$id']; 
    return obj; 
} 
2

To jest rzeczywiście bardzo proste, jeśli skorzystać parametru JSON.parse: reviver:

// example JSON 
 
var j = '{"$id":"0","name":"Parent","child":{"$id":"1", "name":"Child","parent":{"$ref":"0"}},"nullValue":null}' 
 

 
function parseAndResolve(json) { 
 
    var refMap = {}; 
 

 
    return JSON.parse(json, function (key, value) { 
 
     if (key === '$id') { 
 
      refMap[value] = this; 
 
      // return undefined so that the property is deleted 
 
      return void(0); 
 
     } 
 

 
     if (value && value.$ref) { return refMap[value.$ref]; } 
 

 
     return value; 
 
    }); 
 
} 
 

 
console.log(parseAndResolve(j));

+0

Witam, to działa dobrze, ale mam błąd z polem obiektu ustawionym na null. Replace "if (value. $ Ref)" przez "if (value && value. $ Ref)" rozwiąż to :-) – Arcord

+0

@Arcord Dziękuję za zrozumienie tego i poinformowanie mnie! Odpowiedź zaktualizowana. – JLRishe