Analisando JSON usando Json.net

111

Estou tentando analisar alguns JSON usando a biblioteca JSon.Net. A documentação parece um pouco esparsa e estou confuso sobre como realizar o que preciso. Aqui está o formato do JSON que preciso analisar.

{
    "displayFieldName" : "OBJECT_NAME", 
    "fieldAliases" : {
        "OBJECT_NAME" : "OBJECT_NAME", 
        "OBJECT_TYPE" : "OBJECT_TYPE"
    }, 
    "positionType" : "point", 
    "reference" : {
        "id" : 1111
    }, 
    "objects" : [ {
        "attributes" : {
            "OBJECT_NAME" : "test name", 
            "OBJECT_TYPE" : "test type"
        }, 
        "position" : {
            "x" : 5, 
            "y" : 7
        }
    } ]
}

Os únicos dados de que realmente preciso são os dados da matriz de objetos. É possível analisar isso com algo como o JSonTextReader e apenas retirar as coisas que eu quero, como OBJECT_TYPE e as posições xey? Parece que não consigo JSonTextReadertrabalhar da maneira que desejo e encontro poucos ou nenhum exemplo de uso para ele.

Parece que serializar primeiro e depois usar LINQ com meu objeto seria ideal e todos os exemplos que encontro discutem a serialização do JSON primeiro, mas não tenho certeza de como construiria um objeto para essa estrutura. Particularmente a matriz de objetos que precisaria ser algo como uma lista de pares de objetos de atributo e posição. Não tenho ideia de como codificaria meu objeto para que JSon.Net soubesse como serializá-lo.

Achei que poderia escrever meu próprio analisador simples para extrair tudo o que preciso em um objeto de atributos que criei, mas estou tendo pouca sorte.

Esperançosamente, tudo isso faz sentido, alguma ideia?

Carter
fonte

Respostas:

129

Eu não sei sobre JSON.NET, mas funciona muito bem com JavaScriptSerializera partir System.Web.Extensions.dll(.NET 3.5 SP1):

using System.Collections.Generic;
using System.Web.Script.Serialization;
public class NameTypePair
{
    public string OBJECT_NAME { get; set; }
    public string OBJECT_TYPE { get; set; }
}
public enum PositionType { none, point }
public class Ref
{
    public int id { get; set; }
}
public class SubObject
{
    public NameTypePair attributes { get; set; }
    public Position position { get; set; }
}
public class Position
{
    public int x { get; set; }
    public int y { get; set; }
}
public class Foo
{
    public Foo() { objects = new List<SubObject>(); }
    public string displayFieldName { get; set; }
    public NameTypePair fieldAliases { get; set; }
    public PositionType positionType { get; set; }
    public Ref reference { get; set; }
    public List<SubObject> objects { get; set; }
}
static class Program
{

    const string json = @"{
  ""displayFieldName"" : ""OBJECT_NAME"", 
  ""fieldAliases"" : {
    ""OBJECT_NAME"" : ""OBJECT_NAME"", 
    ""OBJECT_TYPE"" : ""OBJECT_TYPE""
  }, 
  ""positionType"" : ""point"", 
  ""reference"" : {
    ""id"" : 1111
  }, 
  ""objects"" : [
    {
      ""attributes"" : {
        ""OBJECT_NAME"" : ""test name"", 
        ""OBJECT_TYPE"" : ""test type""
      }, 
      ""position"" : 
      {
        ""x"" : 5, 
        ""y"" : 7
      }
    }
  ]
}";


    static void Main()
    {
        JavaScriptSerializer ser = new JavaScriptSerializer();
        Foo foo = ser.Deserialize<Foo>(json);
    }


}

Editar:

Json.NET funciona usando o mesmo JSON e classes.

Foo foo = JsonConvert.DeserializeObject<Foo>(json);

Link: serializando e desserializando JSON com Json.NET

Marc Gravell
fonte
13
existe uma maneira de converter pares de valor de nome em uma string JSON em um tipo de variável C # existente (por exemplo, Array ou Dicionário?) de forma que não seja necessário criar classes específicas / personalizadas? No meu caso, a string JSON seria gerada em Ruby / Rails ...
Greg
1
Não quero ter que construir um monte de classes para desserializar - há um equivalente para XElementque me permita usar LINQ nos objetos JSON?
GraemeF
@Marc Gravell: Muito obrigado! Eu não sabia nada sobre JSon. Mas este exemplo me permitiu criar facilmente um arquivo de configuração (com estrutura hierárquica) para minha aplicação.
Peter17
1
@ Peter17: Você não deve usar JSON para configuração; há uma infraestrutura na estrutura .NET que permite seções de configuração hierárquica no arquivo de configuração; com sua solução personalizada, as pessoas agora precisam se preocupar com o arquivo .config e sua seção de configuração personalizada.
casperOne
1
Como obter valores internos de "Object_name" e "Object_Type", ou seja, "" nome do teste "" e "" tipo de teste ""? Você poderia editar sua solução para isso?
AskMe
10

Edit: Obrigado Marc, leia sobre a questão da estrutura vs classe e você está certo, obrigado!

Eu tendo a usar o seguinte método para fazer o que você descreve, usando um método estático de JSon.Net:

MyObject deserializedObject = JsonConvert.DeserializeObject<MyObject>(json);

Link: serializando e desserializando JSON com Json.NET

Para a lista de objetos, posso sugerir o uso de listas genéricas feitas de sua própria pequena classe contendo attributese positionclasse. Você pode usar a Pointestrutura em System.Drawing( System.Drawing.Pointou System.Drawing.PointFpara números de ponto flutuante) para você X e Y.

Após a criação do objeto, é muito mais fácil obter os dados que você deseja do que a análise de texto que você está procurando.

Jarrodn
fonte
structs raramente (ou nunca) seriam uma boa escolha aqui; aderir a objetos (classes).
Marc Gravell
3

(Esta pergunta apareceu no topo de um resultado de mecanismo de pesquisa, mas acabei usando uma abordagem diferente. Adicionando uma resposta a esta pergunta antiga no caso de outras pessoas com perguntas semelhantes lerem isso)

Você pode resolver isso com Json.Net e fazer um método de extensão para lidar com os itens que deseja fazer um loop:

public static Tuple<string, int, int> ToTuple(this JToken token)
{
    var type = token["attributes"]["OBJECT_TYPE"].ToString();
    var x = token["position"]["x"].Value<int>();
    var y = token["position"]["y"].Value<int>();
    return new Tuple<string, int, int>(type, x, y);
}

Em seguida, acesse os dados como este: (cenário: gravação no console):

var tuples = JObject.Parse(myJsonString)["objects"].Select(item => item.ToTuple()).ToList();
tuples.ForEach(t => Console.WriteLine("{0}: ({1},{2})", t.Item1, t.Item2, t.Item3));
PMBjornerud
fonte
2
/*
     * This method takes in JSON in the form returned by javascript's
     * JSON.stringify(Object) and returns a string->string dictionary.
     * This method may be of use when the format of the json is unknown.
     * You can modify the delimiters, etc pretty easily in the source
     * (sorry I didn't abstract it--I have a very specific use).
     */ 
    public static Dictionary<string, string> jsonParse(string rawjson)
    {
        Dictionary<string, string> outdict = new Dictionary<string, string>();
        StringBuilder keybufferbuilder = new StringBuilder();
        StringBuilder valuebufferbuilder = new StringBuilder();
        StringReader bufferreader = new StringReader(rawjson);

        int s = 0;
        bool reading = false;
        bool inside_string = false;
        bool reading_value = false;
        //break at end (returns -1)
        while (s >= 0)
        {
            s = bufferreader.Read();
            //opening of json
            if (!reading)
            {
                if ((char)s == '{' && !inside_string && !reading) reading = true;
                continue;
            }
            else
            {
                //if we find a quote and we are not yet inside a string, advance and get inside
                if (!inside_string)
                {
                    //read past the quote
                    if ((char)s == '\"') inside_string = true;
                    continue;
                }
                if (inside_string)
                {
                    //if we reached the end of the string
                    if ((char)s == '\"')
                    {
                        inside_string = false;
                        s = bufferreader.Read(); //advance pointer
                        if ((char)s == ':')
                        {
                            reading_value = true;
                            continue;
                        }
                        if (reading_value && (char)s == ',')
                        {
                            //we know we just ended the line, so put itin our dictionary
                            if (!outdict.ContainsKey(keybufferbuilder.ToString())) outdict.Add(keybufferbuilder.ToString(), valuebufferbuilder.ToString());
                            //and clear the buffers
                            keybufferbuilder.Clear();
                            valuebufferbuilder.Clear();
                            reading_value = false;
                        }
                        if (reading_value && (char)s == '}')
                        {
                            //we know we just ended the line, so put itin our dictionary
                            if (!outdict.ContainsKey(keybufferbuilder.ToString())) outdict.Add(keybufferbuilder.ToString(), valuebufferbuilder.ToString());
                            //and clear the buffers
                            keybufferbuilder.Clear();
                            valuebufferbuilder.Clear();
                            reading_value = false;
                            reading = false;
                            break;
                        }
                    }
                    else
                    {
                        if (reading_value)
                        {
                            valuebufferbuilder.Append((char)s);
                            continue;
                        }
                        else
                        {
                            keybufferbuilder.Append((char)s);
                            continue;
                        }
                    }
                }
                else
                {
                    switch ((char)s)
                    {
                        case ':':
                            reading_value = true;
                            break;
                        default:
                            if (reading_value)
                            {
                                valuebufferbuilder.Append((char)s);
                            }
                            else
                            {
                                keybufferbuilder.Append((char)s);
                            }
                            break;
                    }
                }
            }
        }
        return outdict;
    }
Sam Grondahl
fonte
Embora essa resposta pareça funcionar bem para JSON sem matrizes / listas, ela não pode lidar de forma alguma com a presença de caracteres [ou ](matriz delimitadora ou estruturas de lista).
Molho Especial
Parece que você está reimplementando a desserialização JSON aqui. Acho que essa é uma solução muito ruim para o problema por muitos motivos diferentes. Veja as respostas mais votadas para melhores abordagens.
Zero3
-1

Você usa a JSONclasse e depois chama a GetData()função.

/// <summary>
/// This class encodes and decodes JSON strings.
/// Spec. details, see http://www.json.org/
///
/// JSON uses Arrays and Objects. These correspond here to the datatypes ArrayList and Hashtable.
/// All numbers are parsed to doubles.
/// </summary>
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Text;

public class JSON
{
    public const int TOKEN_NONE = 0;
    public const int TOKEN_CURLY_OPEN = 1;
    public const int TOKEN_CURLY_CLOSE = 2;
    public const int TOKEN_SQUARED_OPEN = 3;
    public const int TOKEN_SQUARED_CLOSE = 4;
    public const int TOKEN_COLON = 5;
    public const int TOKEN_COMMA = 6;
    public const int TOKEN_STRING = 7;
    public const int TOKEN_NUMBER = 8;
    public const int TOKEN_TRUE = 9;
    public const int TOKEN_FALSE = 10;
    public const int TOKEN_NULL = 11;

    private const int BUILDER_CAPACITY = 2000;

    /// <summary>
    /// Parses the string json into a value
    /// </summary>
    /// <param name="json">A JSON string.</param>
    /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
    public static object JsonDecode(string json)
    {
        bool success = true;

        return JsonDecode(json, ref success);
    }

    /// <summary>
    /// Parses the string json into a value; and fills 'success' with the successfullness of the parse.
    /// </summary>
    /// <param name="json">A JSON string.</param>
    /// <param name="success">Successful parse?</param>
    /// <returns>An ArrayList, a Hashtable, a double, a string, null, true, or false</returns>
    public static object JsonDecode(string json, ref bool success)
    {
        success = true;
        if (json != null) {
            char[] charArray = json.ToCharArray();
            int index = 0;
            object value = ParseValue(charArray, ref index, ref success);
            return value;
        } else {
            return null;
        }
    }

    /// <summary>
    /// Converts a Hashtable / ArrayList object into a JSON string
    /// </summary>
    /// <param name="json">A Hashtable / ArrayList</param>
    /// <returns>A JSON encoded string, or null if object 'json' is not serializable</returns>
    public static string JsonEncode(object json)
    {
        StringBuilder builder = new StringBuilder(BUILDER_CAPACITY);
        bool success = SerializeValue(json, builder);
        return (success ? builder.ToString() : null);
    }

    protected static Hashtable ParseObject(char[] json, ref int index, ref bool success)
    {
        Hashtable table = new Hashtable();
        int token;

        // {
        NextToken(json, ref index);

        bool done = false;
        while (!done) {
            token = LookAhead(json, index);
            if (token == JSON.TOKEN_NONE) {
                success = false;
                return null;
            } else if (token == JSON.TOKEN_COMMA) {
                NextToken(json, ref index);
            } else if (token == JSON.TOKEN_CURLY_CLOSE) {
                NextToken(json, ref index);
                return table;
            } else {

                // name
                string name = ParseString(json, ref index, ref success);
                if (!success) {
                    success = false;
                    return null;
                }

                // :
                token = NextToken(json, ref index);
                if (token != JSON.TOKEN_COLON) {
                    success = false;
                    return null;
                }

                // value
                object value = ParseValue(json, ref index, ref success);
                if (!success) {
                    success = false;
                    return null;
                }

                table[name] = value;
            }
        }

        return table;
    }

    protected static ArrayList ParseArray(char[] json, ref int index, ref bool success)
    {
        ArrayList array = new ArrayList();

        // [
        NextToken(json, ref index);

        bool done = false;
        while (!done) {
            int token = LookAhead(json, index);
            if (token == JSON.TOKEN_NONE) {
                success = false;
                return null;
            } else if (token == JSON.TOKEN_COMMA) {
                NextToken(json, ref index);
            } else if (token == JSON.TOKEN_SQUARED_CLOSE) {
                NextToken(json, ref index);
                break;
            } else {
                object value = ParseValue(json, ref index, ref success);
                if (!success) {
                    return null;
                }

                array.Add(value);
            }
        }

        return array;
    }

    protected static object ParseValue(char[] json, ref int index, ref bool success)
    {
        switch (LookAhead(json, index)) {
            case JSON.TOKEN_STRING:
                return ParseString(json, ref index, ref success);
            case JSON.TOKEN_NUMBER:
                return ParseNumber(json, ref index, ref success);
            case JSON.TOKEN_CURLY_OPEN:
                return ParseObject(json, ref index, ref success);
            case JSON.TOKEN_SQUARED_OPEN:
                return ParseArray(json, ref index, ref success);
            case JSON.TOKEN_TRUE:
                NextToken(json, ref index);
                return true;
            case JSON.TOKEN_FALSE:
                NextToken(json, ref index);
                return false;
            case JSON.TOKEN_NULL:
                NextToken(json, ref index);
                return null;
            case JSON.TOKEN_NONE:
                break;
        }

        success = false;
        return null;
    }

    protected static string ParseString(char[] json, ref int index, ref bool success)
    {
        StringBuilder s = new StringBuilder(BUILDER_CAPACITY);
        char c;

        EatWhitespace(json, ref index);

        // "
        c = json[index++];

        bool complete = false;
        while (!complete) {

            if (index == json.Length) {
                break;
            }

            c = json[index++];
            if (c == '"') {
                complete = true;
                break;
            } else if (c == '\\') {

                if (index == json.Length) {
                    break;
                }
                c = json[index++];
                if (c == '"') {
                    s.Append('"');
                } else if (c == '\\') {
                    s.Append('\\');
                } else if (c == '/') {
                    s.Append('/');
                } else if (c == 'b') {
                    s.Append('\b');
                } else if (c == 'f') {
                    s.Append('\f');
                } else if (c == 'n') {
                    s.Append('\n');
                } else if (c == 'r') {
                    s.Append('\r');
                } else if (c == 't') {
                    s.Append('\t');
                } else if (c == 'u') {
                    int remainingLength = json.Length - index;
                    if (remainingLength >= 4) {
                        // parse the 32 bit hex into an integer codepoint
                        uint codePoint;
                        if (!(success = UInt32.TryParse(new string(json, index, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out codePoint))) {
                            return "";
                        }
                        // convert the integer codepoint to a unicode char and add to string
                        s.Append(Char.ConvertFromUtf32((int)codePoint));
                        // skip 4 chars
                        index += 4;
                    } else {
                        break;
                    }
                }

            } else {
                s.Append(c);
            }

        }

        if (!complete) {
            success = false;
            return null;
        }

        return s.ToString();
    }

    protected static double ParseNumber(char[] json, ref int index, ref bool success)
    {
        EatWhitespace(json, ref index);

        int lastIndex = GetLastIndexOfNumber(json, index);
        int charLength = (lastIndex - index) + 1;

        double number;
        success = Double.TryParse(new string(json, index, charLength), NumberStyles.Any, CultureInfo.InvariantCulture, out number);

        index = lastIndex + 1;
        return number;
    }

    protected static int GetLastIndexOfNumber(char[] json, int index)
    {
        int lastIndex;

        for (lastIndex = index; lastIndex < json.Length; lastIndex++) {
            if ("0123456789+-.eE".IndexOf(json[lastIndex]) == -1) {
                break;
            }
        }
        return lastIndex - 1;
    }

    protected static void EatWhitespace(char[] json, ref int index)
    {
        for (; index < json.Length; index++) {
            if (" \t\n\r".IndexOf(json[index]) == -1) {
                break;
            }
        }
    }

    protected static int LookAhead(char[] json, int index)
    {
        int saveIndex = index;
        return NextToken(json, ref saveIndex);
    }

    protected static int NextToken(char[] json, ref int index)
    {
        EatWhitespace(json, ref index);

        if (index == json.Length) {
            return JSON.TOKEN_NONE;
        }

        char c = json[index];
        index++;
        switch (c) {
            case '{':
                return JSON.TOKEN_CURLY_OPEN;
            case '}':
                return JSON.TOKEN_CURLY_CLOSE;
            case '[':
                return JSON.TOKEN_SQUARED_OPEN;
            case ']':
                return JSON.TOKEN_SQUARED_CLOSE;
            case ',':
                return JSON.TOKEN_COMMA;
            case '"':
                return JSON.TOKEN_STRING;
            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
            case '-':
                return JSON.TOKEN_NUMBER;
            case ':':
                return JSON.TOKEN_COLON;
        }
        index--;

        int remainingLength = json.Length - index;

        // false
        if (remainingLength >= 5) {
            if (json[index] == 'f' &&
                json[index + 1] == 'a' &&
                json[index + 2] == 'l' &&
                json[index + 3] == 's' &&
                json[index + 4] == 'e') {
                index += 5;
                return JSON.TOKEN_FALSE;
            }
        }

        // true
        if (remainingLength >= 4) {
            if (json[index] == 't' &&
                json[index + 1] == 'r' &&
                json[index + 2] == 'u' &&
                json[index + 3] == 'e') {
                index += 4;
                return JSON.TOKEN_TRUE;
            }
        }

        // null
        if (remainingLength >= 4) {
            if (json[index] == 'n' &&
                json[index + 1] == 'u' &&
                json[index + 2] == 'l' &&
                json[index + 3] == 'l') {
                index += 4;
                return JSON.TOKEN_NULL;
            }
        }

        return JSON.TOKEN_NONE;
    }

    protected static bool SerializeValue(object value, StringBuilder builder)
    {
        bool success = true;

        if (value is string) {
            success = SerializeString((string)value, builder);
        } else if (value is Hashtable) {
            success = SerializeObject((Hashtable)value, builder);
        } else if (value is ArrayList) {
            success = SerializeArray((ArrayList)value, builder);
        } else if ((value is Boolean) && ((Boolean)value == true)) {
            builder.Append("true");
        } else if ((value is Boolean) && ((Boolean)value == false)) {
            builder.Append("false");
        } else if (value is ValueType) {
            // thanks to ritchie for pointing out ValueType to me
            success = SerializeNumber(Convert.ToDouble(value), builder);
        } else if (value == null) {
            builder.Append("null");
        } else {
            success = false;
        }
        return success;
    }

    protected static bool SerializeObject(Hashtable anObject, StringBuilder builder)
    {
        builder.Append("{");

        IDictionaryEnumerator e = anObject.GetEnumerator();
        bool first = true;
        while (e.MoveNext()) {
            string key = e.Key.ToString();
            object value = e.Value;

            if (!first) {
                builder.Append(", ");
            }

            SerializeString(key, builder);
            builder.Append(":");
            if (!SerializeValue(value, builder)) {
                return false;
            }

            first = false;
        }

        builder.Append("}");
        return true;
    }

    protected static bool SerializeArray(ArrayList anArray, StringBuilder builder)
    {
        builder.Append("[");

        bool first = true;
        for (int i = 0; i < anArray.Count; i++) {
            object value = anArray[i];

            if (!first) {
                builder.Append(", ");
            }

            if (!SerializeValue(value, builder)) {
                return false;
            }

            first = false;
        }

        builder.Append("]");
        return true;
    }

    protected static bool SerializeString(string aString, StringBuilder builder)
    {
        builder.Append("\"");

        char[] charArray = aString.ToCharArray();
        for (int i = 0; i < charArray.Length; i++) {
            char c = charArray[i];
            if (c == '"') {
                builder.Append("\\\"");
            } else if (c == '\\') {
                builder.Append("\\\\");
            } else if (c == '\b') {
                builder.Append("\\b");
            } else if (c == '\f') {
                builder.Append("\\f");
            } else if (c == '\n') {
                builder.Append("\\n");
            } else if (c == '\r') {
                builder.Append("\\r");
            } else if (c == '\t') {
                builder.Append("\\t");
            } else {
                int codepoint = Convert.ToInt32(c);
                if ((codepoint >= 32) && (codepoint <= 126)) {
                    builder.Append(c);
                } else {
                    builder.Append("\\u" + Convert.ToString(codepoint, 16).PadLeft(4, '0'));
                }
            }
        }

        builder.Append("\"");
        return true;
    }

    protected static bool SerializeNumber(double number, StringBuilder builder)
    {
        builder.Append(Convert.ToString(number, CultureInfo.InvariantCulture));
        return true;
    }
}

//parse and show entire json in key-value pair
    Hashtable HTList = (Hashtable)JSON.JsonDecode("completejsonstring");
        public void GetData(Hashtable HT)
        {           
            IDictionaryEnumerator ienum = HT.GetEnumerator();
            while (ienum.MoveNext())
            {
                if (ienum.Value is ArrayList)
                {
                    ArrayList arnew = (ArrayList)ienum.Value;
                    foreach (object obj in arnew)                    
                    {
                        Hashtable hstemp = (Hashtable)obj;
                        GetData(hstemp);
                    }
                }
                else
                {
                    Console.WriteLine(ienum.Key + "=" + ienum.Value);
                }
            }
        }
APS
fonte