Como resolvo $ ref em um objeto JSON?

5

Eu escrevi um aplicativo de uma única página que usa serviços de descanso para recuperar objetos JSON. Os objetos JSON que estão sendo retornados são objetos C # serializados usando a biblioteca Newtonsoft.JSON. O objeto JSON retornado contém $ ref ponteiros para objetos $ id específicos dentro do mesmo objeto JSON ( pergunta aqui mostra um exemplo ). Eu preciso acessar os dados reais que o $ ref aponta para, mas eu não sei como acessá-lo sem localizar a fonte original.

Encontrei soluções como escrever um novo serializador de contrato e escrever minha própria implementação para resolver as referências. Eu sinto que deveria haver uma solução mais fácil do que esses dois. Eu também explorei as configurações de serialização do Newtonsoft JSON; Eu tentei configurar o PreserveReferenceHandling como None, mas não tive nenhum sucesso lá.

Estou procurando soluções diferentes das que mencionei. Espero que exista uma solução simples que requer muito pouco código para implementar e nenhum framework adicional.

UPDATE

Eu tenho lido um pouco mais sobre isso e descobri que $ ref é um fragmento JSON Pointer. O número de referência é um ID que faz referência a um item de um esquema diferente. Essa parece ser a solução, mas um esquema precisa ser definido para que funcione.

RFC para fragmentos de ponteiro JSON

Informações sobre fragmentos de ponteiro JSON

Referências:
Resolva referências circulares do JSON
< a href="http://james.newtonking.com/json/help/index.html?topic=html/SerializationSettings.htm"> Configurações de serialização

    
por Cameron McKay 26.08.2014 / 15:33
fonte

1 resposta

3

Consegui resolver os links "$ ref" no meu código json adaptando o código, este blogger publicado, para ser compatível com o Angular.

link

Eu criei um serviço angular para resolver o "$ ref" que recebo do serializador NewtonSofts JSON.

// http://willseitz-code.blogspot.com/2013/01/javascript-to-deserialize-json-that.html
(function () {
'use strict';

angular
    .module('app')
    .factory('jsonPointerParseService', jsonPointerParseService);

jsonPointerParseService.$inject = [];

function jsonPointerParseService() {
    var hashOfObjects = {};

    var service = {
        pointerParse: pointerParse
    };

    return service;

    function collectIds(obj) {
        if (jQuery.type(obj) === "object") {
            if (obj.hasOwnProperty("$id")) {
                hashOfObjects[obj.$id] = obj;
            }
            for (var prop in obj) {
                collectIds(obj[prop]);
            }
        } else if (jQuery.type(obj) === "array") {
            obj.forEach(function (element) {
                collectIds(element);
            });
        }
    }

    function setReferences(obj) {
        if (jQuery.type(obj) === "object") {
            for (var prop in obj) {
                if (jQuery.type(obj[prop]) === "object" &&
                    obj[prop].hasOwnProperty("$ref")) {
                    obj[prop] = hashOfObjects[obj[prop]["$ref"]];
                } else {
                    setReferences(obj[prop]);
                }
            }
        } else if (jQuery.type(obj) === "array") {
            obj.forEach(function (element, index, array) {
                if (jQuery.type(element) === "object" &&
                    element.hasOwnProperty("$ref")) {
                    array[index] = hashOfObjects[element["$ref"]];
                } else {
                    setReferences(element);
                }
            });
        }
    }

    // Set the max depth of your object graph because JSON.stringify will not be able to
    // serialize a large object graph back to 
    function setMaxDepth(obj, depth) {
        // If this is not an object or array just return there is no need to 
        // set its max depth.
        if (jQuery.type(obj) !== "array" && jQuery.type(obj) !== "object") {
            return obj;
        }

        var newObj = {};

        // If this object was an array we want to return the same type in order
        // to keep this variable's consistency.
        if (jQuery.type(obj) === "array") {
            newObj = [];
        }

        // For each object or array cut off its tree at the depth value by 
        // recursively diving into the tree.
        angular.forEach(obj, function (value, key) {
            if (depth == 1) {
                newObj = null;
            }
            else if (jQuery.type(value) === "array") {
                if (setMaxDepth(value, depth - 1)) {
                    newObj[key] = setMaxDepth(value, depth - 1)
                } else {
                    newObj = [];
                }
            } else if (jQuery.type(value) === "object") {
                if (setMaxDepth(value, depth - 1)) {
                    newObj[key] = setMaxDepth(value, depth - 1)
                } else {
                    newObj = [];
                }
            } else {
                newObj[key] = value;
            }
        }, newObj);

        return newObj;
    }

    function pointerParse(obj, depth) {
        var newObj = obj;

        hashOfObjects = {};
        collectIds(newObj);
        setReferences(newObj);

        if (depth) {
            newObj = setMaxDepth(newObj, depth);
        }

        return newObj;
    }
}
})();

O uso seria assim:

 var newObj = jsonPointerParseService.pointerParse(obj);

Tenha em mente que este é um serviço Angular. Então, eu o informo em qualquer controlador que precise ter essas referências resolvidas e faça a chamada para pointerParse () de acordo.

Por favor, note que se você quiser postar este objeto de volta para o servidor, a chamada JSON.stringify () do seu navegador deve ser capaz de manipular um objeto com referências circulares. Por padrão, no meu caso, Angular não chama JSON.stringify () com os parâmetros necessários para poder restringir um objeto com referências circulares. Então, neste caso, eu sugiro postar apenas uma pequena parte dos dados e não este objeto inteiro de volta ou instanciar sua própria chamada para restringir um objeto como este.

Finalmente, devo dizer que lidar com $ ref é uma dor e decidi preservar o tratamento de referências apenas com matrizes. Isso pareceu funcionar bem para mim. Eu ainda tinha que acessar valores de matriz com valores $ no entanto eu não precisava se preocupar com a resolução de referências e não precisa se preocupar com o servidor explodir tentando serializar todas as referências circulares do objeto para json.

json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;

json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Arrays;

Atualização: mesmo com apenas as matrizes de resolução, ainda há referências que você pode precisar resolver. Nesses casos, use a função pointerParse () para cuidar deles.

    
por 08.10.2015 / 23:25
fonte