使用 Json.net 解析 JSON

Posted

技术标签:

【中文标题】使用 Json.net 解析 JSON【英文标题】:Parsing JSON using Json.net 【发布时间】:2010-09-28 22:36:03 【问题描述】:

我正在尝试使用 JSon.Net 库解析一些 JSON。文档似乎有点稀疏,我对如何完成我需要的东西感到困惑。这是我需要解析的 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
        
     ]

我真正需要的唯一数据是对象数组中的内容。我是否有可能用 JSonTextReader 之类的东西来解析它,然后取出我想要的东西,比如 OBJECT_TYPE 和 x 和 y 位置?我似乎无法让JSonTextReader 以我想要的方式工作,而且我几乎没有找到它的使用示例。

似乎先进行序列化,然后对我的对象使用 LINQ 是理想的,我发现的每个示例都首先讨论了序列化 JSON,但我不确定如何为这种结构构建对象。特别是对象数组,它需要像一对属性和位置对象的列表。我不知道如何编码我的对象,以便 JSon.Net 知道如何序列化它。

我以为我可以编写自己的简单解析器,将我需要的所有内容提取到我创建的属性对象中,但我运气不佳。

希望这一切都有意义,有什么想法吗?

【问题讨论】:

【参考方案1】:

我不了解 JSON.NET,但它适用于来自 System.Web.Extensions.dll (.NET 3.5 SP1) 的 javascriptSerializer

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);
    



编辑:

Json.NET 使用相同的 JSON 和类工作。

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

链接:Serializing and Deserializing JSON with Json.NET

【讨论】:

有没有办法将 JSON 字符串中的名称值对转换为现有的 C# 变量类型(例如数组或字典?),这样就不必创建特定/自定义类?在我的例子中,JSON 字符串将在 Ruby/Rails 中生成... 我不想构建大量类来反序列化 - 是否有等效于 XElement 的方法可以让我在 JSON 对象上使用 LINQ? @Marc Gravell:非常感谢!我对 JSON 一无所知。但是这个例子让我可以轻松地为我的应用程序创建配置文件(具有层次结构)。 @Peter17:您不应该使用 JSON 进行配置; .NET 框架中有一个基础结构,它允许在配置文件中进行分层配置部分;使用您的自定义解决方案,人们现在不得不担心 .config 文件您的自定义配置部分。 如何从内部 "Object_name" 和 "Object_Type" 即 ""test name"" 和 ""test type"" 中获取值?您能否为此编辑您的解决方案?【参考方案2】:

编辑:感谢 Marc,阅读 struct vs class 问题,你是对的,谢谢!

我倾向于使用以下方法来做你描述的事情,使用 JSon.Net 的静态方法:

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

链接:Serializing and Deserializing JSON with Json.NET

对于对象列表,我可以建议使用由您自己的包含attributesposition 类的小类组成的通用列表。您可以为 X 和 Y 使用 System.Drawing 中的 Point 结构(System.Drawing.PointSystem.Drawing.PointF 用于浮点数)。

在创建对象后,与您正在查看的文本解析相比,获取所需的数据要容易得多

【讨论】:

结构在这里很少(如果有的话)是一个不错的选择;坚持对象(类)。【参考方案3】:

(这个问题在搜索引擎结果中排名靠前,但我最终使用了不同的方法。为这个老问题添加一个答案,以防其他有类似问题的人读到这个)

你可以用 Json.Net 解决这个问题,并制作一个扩展方法来处理你想要循环的项目:

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);

然后像这样访问数据:(场景:写入控制台):

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));

【讨论】:

【参考方案4】:
/*
     * 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;
    

【讨论】:

虽然这个答案似乎适用于没有数组/列表的 JSON,但它根本无法处理 [] 字符(分隔数组或列表结构)的存在。 您似乎在这里重新实现了 JSON 反序列化。由于许多不同的原因,我认为这是一个非常糟糕的问题解决方案。查看最受好评的答案以获得更好的方法。【参考方案5】:

您使用JSON 类,然后调用GetData() 函数。

/// <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);
                
            
        

【讨论】:

以上是关于使用 Json.net 解析 JSON的主要内容,如果未能解决你的问题,请参考以下文章

使用 JSON.net 将 JSON 解析为匿名对象 []

使用 JSON.Net 解析某些数据

Xamarin调用JSON.net来解析JSON

使用Newtonsoft.Json.dll(JSON.NET)动态解析JSON.net 的json的序列化与反序列化

如何在没有 JSON.NET 库的情况下解析 JSON?

在 JSON.NET 数据解析期间忽略解析错误