首页 > 解决方案 > 在 JSON 数组中搜索特定值,然后合并为 XML

问题描述

我只是在不同的数据库中开始这个搜索 JSON 并转换:

/doc1.json:

{
    "seller": "s1",
    "product": [
        "football",
        "basketball"
    ],
    "sales": [
        {
            "football": 60,
            "basketball": [
                {
                    "c1": 76,
                    "c2": 90
                }
            ]
        }
    ]
}

/doc2.json

{
    "seller": "s2",
    "product": ["football"],
    "sales": [
        {"football": 80}
    ]
}

我发现该产品仅包含一个特定项目(如果使用 JavaScript,我会更好),与销售合并,并将结果转换为 XML(使用 XSLT ?)。但我并没有走得很远。首先,我尝试 MarkLogic JS:

cts.search(cts.jsonPropertyValueQuery('product', 'football'))

和许多其他方法,它总是返回 2 个文档。我需要使用模块函数而不是硬编码的 JSON 值。

提前感谢您的帮助。

var doc = cts.doc("/doc1.json");
 var foo = fn.head(xdmp.unquote(
    `
    <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"\n\
        version="2.0">\n\

        <xsl:template match="/">\n\
            <root>\n\
                <xsl:element name="products">\n\
                    <xsl:for-each select="product">\n\
                        <xsl:element name="product">\n\
                            <xsl:variable name="value" select="."/>\n\
                            <xsl:value-of select="."/>\n\
                            <xsl:element name="sales">\n\
                                <xsl:for-each select="/sales/*[name() = $value]">\n\
                                    <xsl:copy select="."/>\n\
                                </xsl:for-each>\n\
                            </xsl:element>\n\
                        </xsl:element>\n\
                    </xsl:for-each>\n\
                </xsl:element>\n\
            </root>\n\
        </xsl:template>\n\

        <xsl:template match="@*|node()">\n\
            <xsl:copy>\n\
                <xsl:apply-templates select="@*|node()"/>\n\
            </xsl:copy>\n\
        </xsl:template>\n\

    </xsl:transform>\n\
    `));

    xdmp.xsltEval(foo, doc);


/doc2.json 应该是:

<root>
  <products>
    <product>football<sales>80</sales>       
    </product>
  </products>
</root>

/doc1.json 应该是:

<root>
  <products>
    <product>football<sales>60</sales></product>
    <product>basketball<sales>
      <c1>76</c1>
      <c2>90</c2>
    </sales></product>
  </products>
</root>

标签: javascriptjsonxsltnosqlxquery

解决方案


SJS:

function valueGetDocUri (collection, propertyPath, propertyValue) {
    const result =[];
    cts.uris("", null, cts.collectionQuery(collection)).toArray().forEach(uri => {
        (cts.values(cts.pathReference(propertyPath), null, null, cts.documentQuery(uri)) == propertyValue) ? result.push(uri) : {}
    });
    return result;
};

执行(这里我有路径索引/indicator):

const try3 = ['profile',  '/indicator',  'SMA'];

valueGetDocUri(...try3)

在 MarkLogic 中,每个 JSON 数组值都是其关联属性的值。 cts.values+cts.pathReference将搜索仅包含指定值词典的路径,在本例中为“SMA”。

样本文件:

  • /profile/multi-indicator.json
{
    "symbol": "USDEUR",
    "date": "2021-08-17",
    "indicator": [
        "BBANDS",       
        "SMA",
        "MACD"
    ] ,
    "riskAdjusted": [
        { "indicator": [  
              {
                "Treynor": 7.19,
                "Jensen": 5.13
              }
           ]
        }
    ],
    "technicalAnalysis": [
        {
            "SMA": 0.8426,
            "MACD": [
                {
                    "MACD_Signal": -0.0007,
                    "MACD_Hist": 0.0026,
                    "MACD": 0.0020
                }
            ],
            "BBANDS": [
                {
                    "realLowerBand": 0.8379,
                    "realUpperBand": 0.8593,
                    "realMiddleBand": 0.8486
                }
            ]
        }
    ]
}

解决第二个问题(在你的情况下,params用你的替换对象:match = 'product'; merge = 'sales'...等)

let doc = cts.doc("/profile/multi-indicator.json");
var params = {};
params.match = 'indicator';
params.merge = 'technicalAnalysis'; 

const implJtoX = fn.head(xdmp.unquote(
`
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="schema://fc.fasset/profile"
exclude-result-prefixes="xs" 
version="2.0">

<xsl:param name="match"/>
<xsl:param name="merge"/>

<xsl:template match="/">
    <profile>
        <xsl:apply-templates/>
        <xsl:element name="algorithm">
            <xsl:for-each select="*[name() eq $match]">
                <xsl:element name="indicator">
                    <xsl:variable name="eName" select="normalize-space(data(.))"/>
                    <xsl:value-of select="$eName"/>
                    <xsl:element name="technical">
                        <xsl:for-each
                            select="/*[name() eq $merge]/*[name() eq $eName]">
                            <xsl:choose>
                                <xsl:when test="*">
                                    <xsl:for-each select="./node()">
                                        <xsl:element name="{normalize-space(name(.))}">
                                            <xsl:value-of select="."/>
                                        </xsl:element>
                                    </xsl:for-each>
                                </xsl:when>
                                <xsl:otherwise>
                                    <xsl:value-of select="."/>
                                </xsl:otherwise>
                            </xsl:choose> 
                        </xsl:for-each> 
                    </xsl:element>
                </xsl:element>
            </xsl:for-each>
        </xsl:element>
    </profile>
</xsl:template>

<xsl:template match="node()">
    <xsl:apply-templates/>
</xsl:template>

</xsl:transform>
`));

const JtoX = xdmp.xsltEval(implJtoX, doc, params);
JtoX

从工程的角度来看,您调用JavaScript 函数和 XSL,在搜索结果中传入迭代文档和转换参数。我让你打包 SJS 和 XSL 模块。如果你完成了这个准备,我不明白为什么你不应该尝试多名称空间 XSL 转换( xdmp:dialect="1.0-ml"[MarkLogic 上的责任] 或没有[XSLT 编辑器上的责任])。转换后的模型如下:

<prof:profile xmlns:prof="schema://fc.fasset/profile" xmlns:meta="schema://fc.fasset/svm/meta">
  <meta:header>
    <meta:IDV1>/svm/d91810d-158-494-ad3-e5afc35a5.xml</meta:IDV1>
    <meta:symbol>USDEUR</meta:symbol>
    <meta:dateSeries>2021-08-17</meta:dateSeries>
    <meta:performance>
        <meta:Treynor>7.19</meta:Treynor>
        <meta:Jensen>5.13</meta:Jensen>
    </meta:performance>
  </meta:header>
  <prof:algorithm>
    <prof:indicator>BBANDS<prof:technical>
        <prof:realLowerBand>0.8379</prof:realLowerBand>
        <prof:realUpperBand>0.8593</prof:realUpperBand>
        <prof:realMiddleBand>0.8486</prof:realMiddleBand>
    </prof:technical></prof:indicator>
    <prof:indicator>SMA<prof:technical>0.8426</prof:technical></prof:indicator>
    <prof:indicator>MACD<prof:technical>
        <prof:MACD_Signal>-0.0007</prof:MACD_Signal>
        <prof:MACD_Hist>0.0026</prof:MACD_Hist>
        <prof:MACD>0.002</prof:MACD>
    </prof:technical></prof:indicator>
  </prof:algorithm>
</prof:profile>

当然,JavaScript 模块也有它的 XQuery 等价物。它们可以互换地评估彼此或 XSL 模块。尽管 SJS 和 XQY 调用之间存在细微差别,但 XSLT 非常适合此类 XML 转换任务。


推荐阅读