web-dev-qa-db-ja.com

JSON.NetXmlシリアル化は配列を誤解しています

自動生成されたxmlがいくつかあり、xmlの一部に複数の行がある場合とない場合があります。その結果、1つの行がある場合は単一のjsonノードが返され、複数の行がある場合はjsonノードを含む配列が返されます。

Xmlは次のようになります

<List>
    <Content>
        <Row Index="0">
            <Title>Testing</Title>
            <PercentComplete>0</PercentComplete>
            <DueDate/>
            <StartDate/>
        </Row>
    </Content>
</List>

または複数の行がある

<List>
    <Content>
        <Row Index="0">
            <Title>Update Documentation</Title>
            <PercentComplete>0.5</PercentComplete>
            <DueDate>2013-01-31 00:00:00</DueDate>
            <StartDate>2013-01-01 00:00:00</StartDate>
        </Row>
        <Row Index="1">
            <Title>Write jQuery example</Title>
            <PercentComplete>0.05</PercentComplete>
            <DueDate>2013-06-30 00:00:00</DueDate>
            <StartDate>2013-01-02 00:00:00</StartDate>
        </Row>
    </Content>
</List>

を使用してこれらをJSONにシリアル化する場合

JsonConvert.SerializeXmlNode(xmldoc, Formatting.Indented);

最初のxmlはこれになります

{
    "List": {
        "Content": {
            "Row": {
                "@Index": "0",
                "Title": "Testing",
                "PercentComplete": "0",
                "DueDate": null,
                "StartDate": null
            }
        }
    }
}

そして2番目のこれ

{
    "List": {
        "Content": {
            "Row": [{
                "@Index": "0",
                "Title": "Update Documentation",
                "PercentComplete": "0.5",
                "DueDate": "2013-01-31 00:00:00",
                "StartDate": "2013-01-01 00:00:00"
            }, {
                "@Index": "1",
                "Title": "Write jQuery example",
                "PercentComplete": "0.05",
                "DueDate": "2013-06-30 00:00:00",
                "StartDate": "2013-01-02 00:00:00"
            }]
        }
    }
}

はっきりとわかるように、2番目の行は本来あるべき配列ですが、最初の行はそうではありません。この種の問題に関する既知の回避策はありますか、それともJSONを受信するフロントエンドにチェックを実装する必要がありますか(構造が非常に動的であるため、少し問題があります)。最良の方法は、json.netに常に配列を返すように強制する方法がある場合です。

24
Eric Herlitz

私はこのようにこの動作を修正しました

// Handle JsonConvert array bug
var rows = doc.SelectNodes("//Row");
if(rows.Count == 1)
{
    var contentNode = doc.SelectSingleNode("//List/Content");
    contentNode.AppendChild(doc.CreateNode("element", "Row", ""));

    // Convert to JSON and replace the empty element we created but keep the array declaration
    returnJson = JsonConvert.SerializeXmlNode(doc).Replace(",null]", "]");
}
else
{
    // Convert to JSON
    returnJson = JsonConvert.SerializeXmlNode(doc);
}

少し汚れていますが、動作します。私はまだ他の解決策に興味があります!

10
Eric Herlitz

Json.NETドキュメントから: http://james.newtonking.com/projects/json/help/?topic=html/ConvertingJSONandXML.htm

JSONに変換するXMLノードに属性json:Array='true'を追加することで、ノードを配列として強制的にレンダリングできます。また、XMLヘッダーxmlns:json='http://james.newtonking.com/projects/json'でjsonプレフィックス名前空間を宣言する必要があります。そうしないと、jsonプレフィックスが宣言されていないことを示すXMLエラーが発生します。

次の例は、ドキュメントによって提供されます。

xml = @"<person xmlns:json='http://james.newtonking.com/projects/json' id='1'>
        <name>Alan</name>
        <url>http://www.google.com</url>
        <role json:Array='true'>Admin</role>
      </person>";

生成された出力:

{
  "person": {
    "@id": "1",
    "name": "Alan",
    "url": "http://www.google.com",
    "role": [
      "Admin"
    ]
  }
}
24

IvánPérezGómezに+1を与え、彼の答えをサポートするためにここにいくつかのコードを提供します。

必要なjson.net名前空間をルートノードに追加します。

private static void AddJsonNetRootAttribute(XmlDocument xmlD)
    {
        XmlAttribute jsonNS = xmlD.CreateAttribute("xmlns", "json", "http://www.w3.org/2000/xmlns/");
        jsonNS.Value = "http://james.newtonking.com/projects/json";

        xmlD.DocumentElement.SetAttributeNode(jsonNS);
    }

そして、xpathによって検出された要素にjson:Array属性を追加するには:

private static void AddJsonArrayAttributesForXPath(string xpath, XmlDocument doc)
    {
        var elements = doc.SelectNodes(xpath);



        foreach (var element in elements)
        {
            var el = element as XmlElement;

            if (el != null)
            {

                var jsonArray = doc.CreateAttribute("json", "Array", "http://james.newtonking.com/projects/json");
                jsonArray.Value = "true";
                el.SetAttributeNode(jsonArray);
            }
        }
    }

これは、json配列としての単一の子ノードのサンプルです。

Here is a sample of a single child node as a json array:

XDocumentを使用して同じ問題が見つかりました

if(XDocument.Parse( "5.0021.0045.00")。Descendants( "row")。Count()> 1){}

            if (XDocument.Parse("<RUT3><row><FromKG>1.00</FromKG><ToKG>5.00</ToKG><Rate>45.00</Rate></row><row><FromKG>6.00</FromKG><ToKG>10.00</ToKG><Rate>65.00</Rate></row><row><FromKG>11.00</FromKG><ToKG>100.00</ToKG><Rate>98.00</Rate></row></RUT3>").Descendants("row").Count() > 1)
            {

            }
0
Pratik 1020

私の解決策:JsonConvertが機能しない場合は、使用しないでください。 XMLを辞書/コレクションに解析してからJsonに解析します。少なくともこの方法では、要素名をハードコーディングする必要はありません。

    private JsonResult AsJsonResult(XmlDocument result)
    {
        var kvp = new KeyValuePair<string, object>(result.DocumentElement.Name, Value(result.DocumentElement));

        return Json(kvp
             , JsonRequestBehavior.AllowGet);
    }

    /// <summary>
    /// Deserializing straight from Xml produces Ugly Json, convert to Dictionaries first to strip out unwanted nesting
    /// </summary>
    /// <param name="node"></param>
    /// <returns></returns>
    private object Value(XmlNode node)
    {
        dynamic value;

        //If we hit a complex element
        if (node.HasChildNodes && !(node.FirstChild is XmlText))
        {
            //If we hit a collection, it will have children which are also not just text!
            if (node.FirstChild.HasChildNodes && !(node.FirstChild.FirstChild is XmlText))
            {
                //want to return a list of Dictionarys for the children's nodes
                //Eat one level of the hierachy and return child nodes as an array
                value = new List<object>();
                foreach (XmlNode childNode in node.ChildNodes)
                {
                    value.Add(Value(childNode));
                }
            }
            else //regular complex element return childNodes as a dictionary
            {
                value = new Dictionary<string, object>();
                foreach (XmlNode childNode in node.ChildNodes)
                {
                    value.Add(childNode.Name, Value(childNode));
                }
            }
        }
        else //Simple element
        {
            value = node.FirstChild.InnerText;
        }

        return value;
    }
0
waxingsatirical

JsonConvert.DeserializeXmlNodeで、使用可能な配列ノードにboolパラメーターを追加する方が簡単です。

  var xml= JsonConvert.DeserializeXmlNode(dashstring, "root", true);
0
Harlin Acero